Passed
Push — master ( d3cc39...f2a84f )
by Melech
03:59
created

NullSession::validateId()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 7
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Valkyrja Framework package.
7
 *
8
 * (c) Melech Mizrachi <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Valkyrja\Session;
15
16
use Exception;
17
use Valkyrja\Session\Contract\Session as Contract;
18
use Valkyrja\Session\Exception\InvalidCsrfToken;
19
use Valkyrja\Session\Exception\InvalidSessionId;
20
use Valkyrja\Session\Exception\SessionStartFailure;
21
22
use function bin2hex;
23
use function hash_equals;
24
use function is_string;
25
use function preg_match;
26
use function random_bytes;
27
28
/**
29
 * Class NullSession.
30
 *
31
 * @author Melech Mizrachi
32
 */
33
class NullSession implements Contract
34
{
35
    /**
36
     * The session id.
37
     *
38
     * @var string
39
     */
40
    protected string $id = '';
41
42
    /**
43
     * The session name.
44
     *
45
     * @var string
46
     */
47
    protected string $name = '';
48
49
    /**
50
     * The session data.
51
     *
52
     * @var array<string, mixed>
53
     */
54
    protected array $data = [];
55
56
    /**
57
     * NullSession constructor.
58
     *
59
     * @throws InvalidSessionId
60
     * @throws SessionStartFailure
61
     */
62
    public function __construct(
63
        string|null $sessionId = null,
64
        string|null $sessionName = null
65
    ) {
66
        // If a session id is provided
67
        if (is_string($sessionId)) {
68
            $this->validateId($sessionId);
69
70
            // Set the id
71
            $this->id = $sessionId;
72
        }
73
74
        // If a session name is provided
75
        if (is_string($sessionName)) {
76
            // Set the name
77
            $this->name = $sessionName;
78
        }
79
80
        // Start the session
81
        $this->start();
82
    }
83
84
    /**
85
     * @inheritDoc
86
     */
87
    public function start(): void
88
    {
89
    }
90
91
    /**
92
     * @inheritDoc
93
     */
94
    public function getId(): string
95
    {
96
        return $this->id;
97
    }
98
99
    /**
100
     * @inheritDoc
101
     */
102
    public function setId(string $id): void
103
    {
104
        $this->validateId($id);
105
106
        $this->id = $id;
107
    }
108
109
    /**
110
     * @inheritDoc
111
     */
112
    public function getName(): string
113
    {
114
        return $this->name;
115
    }
116
117
    /**
118
     * @inheritDoc
119
     */
120
    public function setName(string $name): void
121
    {
122
        $this->name = $name;
123
    }
124
125
    /**
126
     * @inheritDoc
127
     */
128
    public function isActive(): bool
129
    {
130
        return true;
131
    }
132
133
    /**
134
     * @inheritDoc
135
     */
136
    public function has(string $id): bool
137
    {
138
        return isset($this->data[$id]);
139
    }
140
141
    /**
142
     * @inheritDoc
143
     */
144
    public function get(string $id, mixed $default = null): mixed
145
    {
146
        return $this->data[$id] ?? $default;
147
    }
148
149
    /**
150
     * @inheritDoc
151
     */
152
    public function set(string $id, $value): void
153
    {
154
        $this->data[$id] = $value;
155
    }
156
157
    /**
158
     * @inheritDoc
159
     */
160
    public function remove(string $id): bool
161
    {
162
        if (! $this->has($id)) {
163
            return false;
164
        }
165
166
        unset($this->data[$id]);
167
168
        return true;
169
    }
170
171
    /**
172
     * @inheritDoc
173
     */
174
    public function all(): array
175
    {
176
        return $this->data;
177
    }
178
179
    /**
180
     * @inheritDoc
181
     *
182
     * @throws Exception
183
     */
184
    public function generateCsrfToken(string $id): string
185
    {
186
        $token = bin2hex(random_bytes(64));
187
188
        $this->set($id, $token);
189
190
        return $token;
191
    }
192
193
    /**
194
     * @inheritDoc
195
     */
196
    public function validateCsrfToken(string $id, string $token): void
197
    {
198
        if (! $this->isCsrfTokenValid($id, $token)) {
199
            throw new InvalidCsrfToken("CSRF token id: `$id` has invalid token of `$token` provided");
200
        }
201
    }
202
203
    /**
204
     * @inheritDoc
205
     */
206
    public function isCsrfTokenValid(string $id, string $token): bool
207
    {
208
        if (! $this->has($id)) {
209
            return false;
210
        }
211
212
        /** @var mixed $sessionToken */
213
        $sessionToken = $this->get($id);
214
215
        if (is_string($sessionToken) && hash_equals($token, $sessionToken)) {
216
            $this->remove($id);
217
218
            return true;
219
        }
220
221
        return false;
222
    }
223
224
    /**
225
     * @inheritDoc
226
     */
227
    public function clear(): void
228
    {
229
        $this->data = [];
230
    }
231
232
    /**
233
     * @inheritDoc
234
     */
235
    public function destroy(): void
236
    {
237
        $this->data = [];
238
    }
239
240
    /**
241
     * Validate an id.
242
     *
243
     * @param string $id The id
244
     *
245
     * @return void
246
     */
247
    protected function validateId(string $id): void
248
    {
249
        if (! preg_match('/^[-,a-zA-Z0-9]{1,128}$/', $id)) {
250
            throw new InvalidSessionId(
251
                "The session id, '$id', is invalid! "
252
                . 'Session id can only contain alpha numeric characters, dashes, commas, '
253
                . 'and be at least 1 character in length but up to 128 characters long.'
254
            );
255
        }
256
    }
257
}
258