Passed
Push — master ( f16b47...733353 )
by
unknown
13:48
created

UserSession::get()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the TYPO3 CMS project.
7
 *
8
 * It is free software; you can redistribute it and/or modify it under
9
 * the terms of the GNU General Public License, either version 2
10
 * of the License, or any later version.
11
 *
12
 * For the full copyright and license information, please read the
13
 * LICENSE.txt file that was distributed with this source code.
14
 *
15
 * The TYPO3 project - inspiring people to share!
16
 */
17
18
namespace TYPO3\CMS\Core\Session;
19
20
/**
21
 * Represents all information about a user's session.
22
 * A user session can be bound to a frontend / backend user, or an anonymous session based on session data stored
23
 * in the session backend.
24
 *
25
 * If a session is anonymous, it can be fixated by storing the session in the backend, but only if there
26
 * is data in the session.
27
 *
28
 * if a session is user-bound, it is automatically fixated.
29
 *
30
 * The $isNew flag is meant to show that this user session object was not fetched from the session backend,
31
 * but initialized in the first place by the current request.
32
 *
33
 * The $data argument is to store any arbitrary data valid for the users' session.
34
 *
35
 * A permanent session means: XYZ?
36
 */
37
class UserSession
38
{
39
    protected const SESSION_UPDATE_GRACE_PERIOD = 61;
40
    protected string $identifier;
41
    protected ?int $userId;
42
    protected int $lastUpdated;
43
    protected array $data;
44
    protected bool $wasUpdated = false;
45
    protected string $ipLock = '';
46
    protected bool $isNew = true;
47
    protected bool $isPermanent = false;
48
49
    protected function __construct(string $identifier, int $userId, int $lastUpdated, array $data = [])
50
    {
51
        $this->identifier = $identifier;
52
        $this->userId = $userId > 0 ? $userId : null;
53
        $this->lastUpdated = $lastUpdated;
54
        $this->data = $data;
55
    }
56
57
    /**
58
     * Get the user session identifier (the ses_id)
59
     *
60
     * @return string
61
     */
62
    public function getIdentifier(): string
63
    {
64
        return $this->identifier;
65
    }
66
67
    /**
68
     * Get the user id (ID of the user record to whom the session belongs)
69
     *
70
     * @return int
71
     */
72
    public function getUserId(): ?int
73
    {
74
        return $this->userId;
75
    }
76
77
    /**
78
     * Get the timestamp of the last session data update
79
     *
80
     * @return int
81
     */
82
    public function getLastUpdated(): int
83
    {
84
        return $this->lastUpdated;
85
    }
86
87
    /**
88
     * Set / update a data value for a given key.
89
     * Throws an exception if the given key is empty.
90
     *
91
     * @param string $key The key whose value should be updated
92
     * @param mixed $value The value or NULL to unset the key
93
     */
94
    public function set(string $key, $value): void
95
    {
96
        if ($key === '') {
97
            throw new \InvalidArgumentException('Argument key must not be empty', 1484312516);
98
        }
99
        if ($value === null) {
100
            unset($this->data[$key]);
101
        } else {
102
            $this->data[$key] = $value;
103
        }
104
        $this->wasUpdated = true;
105
    }
106
107
    /**
108
     * Check whether the session has data
109
     *
110
     * @return bool
111
     */
112
    public function hasData(): bool
113
    {
114
        return $this->data !== [];
115
    }
116
117
    /**
118
     * Return the data for the given key or an NULL if the key does not exist
119
     *
120
     * @param string $key
121
     * @return mixed
122
     */
123
    public function get(string $key)
124
    {
125
        return $this->data[$key] ?? null;
126
    }
127
128
    /**
129
     * Return the whole session data array.
130
     *
131
     * @return array
132
     */
133
    public function getData(): array
134
    {
135
        return $this->data;
136
    }
137
138
    /**
139
     * Override the whole $data. Can be used to e.g. preserve session data
140
     * on login or to remove session data by providing an empty array.
141
     *
142
     * @param array $data
143
     */
144
    public function overrideData(array $data): void
145
    {
146
        if ($this->data !== $data) {
147
            // Only set update flag if there is change in the $data array
148
            $this->wasUpdated = true;
149
        }
150
151
        $this->data = $data;
152
    }
153
154
    /**
155
     * Check if session data was already updated
156
     *
157
     * @return bool
158
     */
159
    public function dataWasUpdated(): bool
160
    {
161
        return $this->wasUpdated;
162
    }
163
164
    /**
165
     * Check if the user session is an anonymous one.
166
     * This means, the session does not belong to a logged-in user.
167
     *
168
     * @return bool
169
     */
170
    public function isAnonymous(): bool
171
    {
172
        return $this->userId === 0 || $this->userId === null;
173
    }
174
175
    /**
176
     * Return the sessions ipLock state
177
     *
178
     * @return string
179
     */
180
    public function getIpLock(): string
181
    {
182
        return $this->ipLock;
183
    }
184
185
    /**
186
     * Check whether the session was marked as new on creation
187
     *
188
     * @return bool
189
     */
190
    public function isNew(): bool
191
    {
192
        return $this->isNew;
193
    }
194
195
    /**
196
     * Check whether the session was marked as permanent on creation
197
     *
198
     * @return bool
199
     */
200
    public function isPermanent(): bool
201
    {
202
        return $this->isPermanent;
203
    }
204
205
    /**
206
     * Use a gracetime-value to avoid updating a session-record too often
207
     *
208
     * @return bool
209
     */
210
    public function needsUpdate(): bool
211
    {
212
        return $GLOBALS['EXEC_TIME'] > ($this->lastUpdated + self::SESSION_UPDATE_GRACE_PERIOD);
213
    }
214
215
    /**
216
     * Create a new user session based on the provided session record
217
     *
218
     * @param string $id the session identifier
219
     * @param array $record
220
     * @param bool $markAsNew
221
     * @return UserSession
222
     */
223
    public static function createFromRecord(string $id, array $record, bool $markAsNew = false): self
224
    {
225
        $userSession = new self(
226
            $id,
227
            (int)($record['ses_userid'] ?? 0),
228
            (int)($record['ses_tstamp'] ?? 0),
229
            unserialize($record['ses_data'] ?? '', ['allowed_classes' => false]) ?: []
230
        );
231
        $userSession->ipLock = $record['ses_iplock'] ?? '';
232
        $userSession->isNew = $markAsNew;
233
        if (isset($record['ses_permanent'])) {
234
            $userSession->isPermanent = (bool)$record['ses_permanent'];
235
        }
236
        return $userSession;
237
    }
238
239
    /**
240
     * Create a non fixated user session. This means the
241
     * session does not belong to a logged-in user.
242
     *
243
     * @param string $identifier
244
     * @return UserSession
245
     */
246
    public static function createNonFixated(string $identifier): self
247
    {
248
        $userSession = new self($identifier, 0, $GLOBALS['EXEC_TIME'], []);
249
        $userSession->isPermanent = false;
250
        $userSession->isNew = true;
251
        return $userSession;
252
    }
253
254
    /**
255
     * Used internally to store data in the backend
256
     *
257
     * @return array The session record as array
258
     */
259
    public function toArray(): array
260
    {
261
        $data = [
262
            'ses_id' => $this->identifier,
263
            'ses_data' => serialize($this->data),
264
            'ses_userid' => (int)$this->userId,
265
            'ses_iplock' => $this->ipLock,
266
            'ses_tstamp' => $this->lastUpdated
267
        ];
268
        if ($this->isPermanent) {
269
            $data['ses_permanent'] = 1;
270
        }
271
        return $data;
272
    }
273
}
274