Passed
Push — master ( 02aa89...f20b28 )
by Cesar
02:07 queued 12s
created

MagicLink::getTokenLength()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 3
c 1
b 0
f 0
dl 0
loc 5
rs 10
cc 2
nc 2
nop 0
1
<?php
2
3
namespace MagicLink;
4
5
use Carbon\Carbon;
6
use Illuminate\Database\Eloquent\Model;
7
use Illuminate\Database\QueryException;
8
use Illuminate\Support\Facades\Event;
9
use Illuminate\Support\Str;
10
use MagicLink\Actions\ActionInterface;
11
use MagicLink\Events\MagicLinkWasCreated;
12
use MagicLink\Events\MagicLinkWasVisited;
13
14
class MagicLink extends Model
15
{
16
    public $incrementing = false;
17
18
    protected $keyType = 'string';
19
20
    protected static function boot()
21
    {
22
        parent::boot();
23
24
        static::creating(function ($model) {
25
            $model->id = Str::uuid();
26
        });
27
    }
28
29
    protected static function getTokenLength()
30
    {
31
        return config('magiclink.token.length', 64) <= 255
32
            ? config('magiclink.token.length', 64)
33
            : 255;
34
    }
35
36
    public function getActionAttribute($value)
37
    {
38
        if ($this->getConnection()->getDriverName() === 'pgsql') {
39
            return unserialize(base64_decode($value));
40
        }
41
42
        return unserialize($value);
43
    }
44
45
    public function setActionAttribute($value)
46
    {
47
        $this->attributes['action'] = $this->getConnection()->getDriverName() === 'pgsql'
48
                                        ? base64_encode(serialize($value))
49
                                        : serialize($value);
50
    }
51
52
    public function getUrlAttribute()
53
    {
54
        return url(sprintf(
55
            '%s/%s:%s',
56
            config('magiclink.url.validate_path', 'magiclink'),
57
            $this->id,
58
            $this->token
59
        ));
60
    }
61
62
    /**
63
     * Create makiglink.
64
     *
65
     * @param ActionInterface $action
66
     * @param int|null $lifetime
67
     * @param int|null $numMaxVisits
68
     * @return self
69
     */
70
    public static function create(ActionInterface $action, ?int $lifetime = 4320, ?int $numMaxVisits = null)
71
    {
72
        self::deleteMagicLinkExpired();
73
74
        $magiclink = new static();
75
76
        $magiclink->token = Str::random(self::getTokenLength());
0 ignored issues
show
Bug introduced by
The property token does not seem to exist on MagicLink\MagicLink. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
77
        $magiclink->available_at = $lifetime
0 ignored issues
show
Bug introduced by
The property available_at does not seem to exist on MagicLink\MagicLink. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
78
                                    ? Carbon::now()->addMinute($lifetime)
0 ignored issues
show
Unused Code introduced by
The call to Carbon\Carbon::addMinute() has too many arguments starting with $lifetime. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

78
                                    ? Carbon::now()->/** @scrutinizer ignore-call */ addMinute($lifetime)

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
79
                                    : null;
80
        $magiclink->max_visits = $numMaxVisits;
0 ignored issues
show
Bug introduced by
The property max_visits does not seem to exist on MagicLink\MagicLink. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
81
        $magiclink->action = $action;
0 ignored issues
show
Bug introduced by
The property action does not seem to exist on MagicLink\MagicLink. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
82
83
        $magiclink->save();
84
85
        Event::dispatch(new MagicLinkWasCreated($magiclink));
86
87
        return $magiclink;
88
    }
89
90
    /**
91
     * Execute Action.
92
     *
93
     * @return \Illuminate\Routing\Redirector|\Illuminate\Http\RedirectResponse
94
     */
95
    public function run()
96
    {
97
        return $this->action->run();
98
    }
99
100
    /**
101
     * Call when magiclink has been visited.
102
     *
103
     * @return void
104
     */
105
    public function visited()
106
    {
107
        try {
108
            $this->increment('num_visits');
109
        } catch (QueryException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
110
        }
111
112
        Event::dispatch(new MagicLinkWasVisited($this));
113
    }
114
115
    /**
116
     * Get valid MagicLink by token.
117
     *
118
     * @param string $token
119
     * @return null|\MagicLink\MagicLink
120
     */
121
    public static function getValidMagicLinkByToken($token)
122
    {
123
        [$tokenId, $tokenSecret] = explode(':', "{$token}:");
124
125
        if (empty($tokenSecret)) {
126
            return;
127
        }
128
129
        return self::where('id', $tokenId)
130
                    ->where('token', $tokenSecret)
131
                    ->where(function ($query) {
132
                        $query
133
                            ->whereNull('available_at')
134
                            ->orWhere('available_at', '>=', Carbon::now());
135
                    })
136
                    ->where(function ($query) {
137
                        $query
138
                            ->whereNull('max_visits')
139
                            ->orWhereRaw('max_visits > num_visits');
140
                    })
141
                    ->first();
142
    }
143
144
    /**
145
     * Get MagicLink by token.
146
     *
147
     * @param string $token
148
     * @return null|\MagicLink\MagicLink
149
     */
150
    public static function getMagicLinkByToken($token)
151
    {
152
        [$tokenId, $tokenSecret] = explode(':', "{$token}:");
153
154
        if (empty($tokenSecret)) {
155
            return;
156
        }
157
158
        return self::where('id', $tokenId)
159
                    ->where('token', $tokenSecret)
160
                    ->first();
161
    }
162
163
    /**
164
     * Delete magiclink was expired.
165
     *
166
     * @return void
167
     */
168
    public static function deleteMagicLinkExpired()
169
    {
170
        self::where(function ($query) {
171
            $query
172
                ->where('available_at', '<', Carbon::now())
173
                ->orWhere(function ($query) {
174
                    $query
175
                        ->whereNotNull('max_visits')
176
                        ->whereRaw('max_visits <= num_visits');
177
                });
178
        })
179
        ->delete();
180
    }
181
182
    /**
183
     * Delete all MagicLink.
184
     *
185
     * @return void
186
     */
187
    public static function deleteAllMagicLink()
188
    {
189
        self::truncate();
190
    }
191
}
192