Guard::getDispatcher()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Magister\Services\Auth;
4
5
use Magister\Services\Contracts\Auth\Authenticable as UserContract;
6
use Magister\Services\Contracts\Auth\Guard as GuardContract;
7
use Magister\Services\Contracts\Auth\UserProvider;
8
use Magister\Services\Contracts\Events\Dispatcher;
9
use Magister\Services\Cookie\CookieJar;
10
11
/**
12
 * Class Guard.
13
 */
14
class Guard implements GuardContract
15
{
16
    /**
17
     * The currently authenticated user.
18
     *
19
     * @var \Magister\Services\Contracts\Auth\Authenticable
20
     */
21
    protected $user;
22
23
    /**
24
     * The user provider implementation.
25
     *
26
     * @var \Magister\Services\Contracts\Auth\UserProvider
27
     */
28
    protected $provider;
29
30
    /**
31
     * The Magister cookie creator service.
32
     *
33
     * @var \Magister\Services\Cookie\CookieJar
34
     */
35
    protected $cookie;
36
37
    /**
38
     * The event dispatcher instance.
39
     *
40
     * @var \Magister\Services\Contracts\Events\Dispatcher
41
     */
42
    protected $events;
43
44
    /**
45
     * Indicates if the logout method has been called.
46
     *
47
     * @var bool
48
     */
49
    protected $loggedOut = false;
50
51
    /**
52
     * Create a new guard instance.
53
     *
54
     * @param \Magister\Services\Contracts\Auth\UserProvider $provider
55
     */
56
    public function __construct(UserProvider $provider)
57
    {
58
        $this->provider = $provider;
59
    }
60
61
    /**
62
     * Determine if the current user is authenticated.
63
     *
64
     * @return bool
65
     */
66
    public function check()
67
    {
68
        return !is_null($this->user());
69
    }
70
71
    /**
72
     * Determine if the current user is a guest.
73
     *
74
     * @return bool
75
     */
76
    public function guest()
77
    {
78
        return !$this->check();
79
    }
80
81
    /**
82
     * Get the currently authenticated user.
83
     *
84
     * @return \Magister\Services\Contracts\Auth\Authenticable|null
85
     */
86
    public function user()
87
    {
88
        if ($this->loggedOut) {
89
            return;
90
        }
91
92
        // If we have already retrieved the user for the current request we can just
93
        // return it back immediately. We do not want to pull the user data every
94
        // request into the method because that would tremendously slow an app.
95
        if (!is_null($this->user)) {
96
            return $this->user;
97
        }
98
99
        $id = $this->cookie->get($this->getName());
100
101
        $user = null;
102
103
        if (!is_null($id)) {
104
            $user = $this->provider->retrieveByToken();
105
        }
106
107
        return $this->user = $user;
0 ignored issues
show
Documentation Bug introduced by
It seems like $user can also be of type object<Magister\Services\Database\Elegant\Model>. However, the property $user is declared as type object<Magister\Services...cts\Auth\Authenticable>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
Bug Compatibility introduced by
The expression $this->user = $user; of type Magister\Services\Database\Elegant\Model|null adds the type Magister\Services\Database\Elegant\Model to the return on line 107 which is incompatible with the return type declared by the interface Magister\Services\Contracts\Auth\Guard::user of type Magister\Services\Contra...Auth\Authenticable|null.
Loading history...
108
    }
109
110
    /**
111
     * Get the id for the currently authenticated user.
112
     *
113
     * @return int|null
114
     */
115
    public function id()
116
    {
117
        if ($this->loggedOut) {
118
            return;
119
        }
120
121
        $id = $this->cookie->get($this->getName());
122
123
        if (is_null($id) && $this->user()) {
124
            $id = $this->user()->getAuthIdentifier();
0 ignored issues
show
Bug introduced by
The method getAuthIdentifier does only exist in Magister\Services\Contracts\Auth\Authenticable, but not in Magister\Services\Database\Elegant\Model.

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

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
125
        }
126
127
        return $id;
128
    }
129
130
    /**
131
     * Attempt to authenticate a user using the given credentials.
132
     *
133
     * @param array $credentials
134
     * @param bool  $login
135
     *
136
     * @return bool
137
     */
138
    public function attempt(array $credentials = [], $login = true)
139
    {
140
        $user = $this->provider->retrieveByCredentials($credentials);
141
142
        if ($this->hasValidCredentials($user)) {
143
            if ($login) {
144
                $this->login($user);
0 ignored issues
show
Documentation introduced by
$user is of type object<Magister\Services...ase\Elegant\Model>|null, but the function expects a object<Magister\Services...cts\Auth\Authenticable>.

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...
145
            }
146
147
            return true;
148
        }
149
150
        return false;
151
    }
152
153
    /**
154
     * Determine if the user matches the credentials.
155
     *
156
     * @param mixed $user
157
     *
158
     * @return bool
159
     */
160
    protected function hasValidCredentials($user)
161
    {
162
        return !is_null($user);
163
    }
164
165
    /**
166
     * Log a user into the application.
167
     *
168
     * @param \Magister\Services\Contracts\Auth\Authenticable $user
169
     *
170
     * @return void
171
     */
172
    public function login(UserContract $user)
173
    {
174
        $this->updateSession($user->getAuthIdentifier());
175
176
        // If we have an event dispatcher instance set we will fire an event so that
177
        // any listeners will hook into the authentication events and run actions
178
        // based on the login and logout events fired from the guard instances.
179
        $this->fireLoginEvent($user);
180
181
        $this->setUser($user);
182
    }
183
184
    /**
185
     * Fire the login event if the dispatcher is set.
186
     *
187
     * @param \Magister\Services\Contracts\Auth\Authenticable $user
188
     *
189
     * @return void
190
     */
191
    protected function fireLoginEvent($user)
192
    {
193
        if (isset($this->events)) {
194
            $this->events->fire('auth.login', $user);
195
        }
196
    }
197
198
    /**
199
     * Update the session with the given cookie.
200
     *
201
     * @param string $id
202
     *
203
     * @return void
204
     */
205
    protected function updateSession($id)
206
    {
207
        $this->cookie->make($this->getName(), $id);
208
    }
209
210
    /**
211
     * Log the user out of the application.
212
     *
213
     * @return void
214
     */
215
    public function logout()
216
    {
217
        $user = $this->user();
218
219
        // If we have an event dispatcher instance, we can fire off the logout event
220
        // so any further processing can be done. This allows the developer to be
221
        // listening for anytime a user signs out of this application manually.
222
        $this->clearUserDataFromStorage();
223
224
        if (isset($this->events)) {
225
            $this->events->fire('auth.logout', $user);
226
        }
227
228
        // Once we have fired the logout event we will clear the users out of memory
229
        // so they are no longer available as the user is no longer considered as
230
        // being signed into this application and should not be available here.
231
        $this->user = null;
232
233
        $this->loggedOut = true;
234
    }
235
236
    /**
237
     * Remove the user data from the session and cookies.
238
     *
239
     * @return void
240
     */
241
    protected function clearUserDataFromStorage()
242
    {
243
        $this->getCookieJar()->forget($this->getName());
244
245
        $recaller = $this->getRecallerName();
246
247
        $this->getCookieJar()->forget($recaller);
248
249
        $this->provider->removeToken();
250
    }
251
252
    /**
253
     * Get the name of the cookie used to store the "recaller".
254
     *
255
     * @return string
256
     */
257
    public function getRecallerName()
258
    {
259
        return 'SESSION_ID';
260
    }
261
262
    /**
263
     * Get a unique identifier for the auth session value.
264
     *
265
     * @return string
266
     */
267
    public function getName()
268
    {
269
        return 'login_'.md5(get_class($this));
270
    }
271
272
    /**
273
     * Set the current user.
274
     *
275
     * @param \Magister\Services\Contracts\Auth\Authenticable $user
276
     *
277
     * @return void
278
     */
279
    public function setUser(UserContract $user)
280
    {
281
        $this->user = $user;
282
283
        $this->loggedOut = false;
284
    }
285
286
    /**
287
     * Return the currently cached user.
288
     *
289
     * @return mixed|null
290
     */
291
    public function getUser()
292
    {
293
        return $this->user;
294
    }
295
296
    /**
297
     * Set the cookie creator instance used by the guard.
298
     *
299
     * @param \Magister\Services\Cookie\CookieJar $cookie
300
     *
301
     * @return void
302
     */
303
    public function setCookieJar(CookieJar $cookie)
304
    {
305
        $this->cookie = $cookie;
306
    }
307
308
    /**
309
     * Get the cookie creator instance used by the guard.
310
     *
311
     * @throws \RuntimeException
312
     *
313
     * @return \Magister\Services\Cookie\CookieJar
314
     */
315
    public function getCookieJar()
316
    {
317
        if (!isset($this->cookie)) {
318
            throw new \RuntimeException('Cookie jar has not been set.');
319
        }
320
321
        return $this->cookie;
322
    }
323
324
    /**
325
     * Set the event dispatcher instance.
326
     *
327
     * @param \Magister\Services\Contracts\Events\Dispatcher $events
328
     *
329
     * @return void
330
     */
331
    public function setDispatcher(Dispatcher $events)
332
    {
333
        $this->events = $events;
334
    }
335
336
    /**
337
     * Get the event dispatcher instance.
338
     *
339
     * @return \Magister\Services\Contracts\Events\Dispatcher
340
     */
341
    public function getDispatcher()
342
    {
343
        return $this->events;
344
    }
345
346
    /**
347
     * Set the user provider used by the guard.
348
     *
349
     * @param \Magister\Services\Contracts\Auth\UserProvider $provider
350
     *
351
     * @return void
352
     */
353
    public function setProvider(UserProvider $provider)
354
    {
355
        $this->provider = $provider;
356
    }
357
358
    /**
359
     * Get the user provider used by the guard.
360
     *
361
     * @return \Magister\Services\Contracts\Auth\UserProvider
362
     */
363
    public function getProvider()
364
    {
365
        return $this->provider;
366
    }
367
}
368