xref: /webtrees/app/SessionDatabaseHandler.php (revision f12c148e9d7c3ff5424745cf359baeb303a4fcbc)
1<?php
2
3/**
4 * webtrees: online genealogy
5 * Copyright (C) 2021 webtrees development team
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18declare(strict_types=1);
19
20namespace Fisharebest\Webtrees;
21
22use Illuminate\Database\Capsule\Manager as DB;
23use Psr\Http\Message\ServerRequestInterface;
24use SessionHandlerInterface;
25
26use function date;
27
28/**
29 * Session handling - stores sessions in the database.
30 */
31class SessionDatabaseHandler implements SessionHandlerInterface
32{
33    private ServerRequestInterface $request;
34
35    private ?object $row;
36
37    /**
38     * SessionDatabaseHandler constructor.
39     *
40     * @param ServerRequestInterface $request
41     */
42    public function __construct(ServerRequestInterface $request)
43    {
44        $this->request = $request;
45    }
46
47    /**
48     * @param string $path
49     * @param string $name
50     *
51     * @return bool
52     */
53    public function open($path, $name): bool
54    {
55        return true;
56    }
57
58    /**
59     * @return bool
60     */
61    public function close(): bool
62    {
63        return true;
64    }
65
66    /**
67     * @param string $id
68     *
69     * @return string
70     */
71    public function read($id): string
72    {
73        $this->row = DB::table('session')
74            ->where('session_id', '=', $id)
75            ->first();
76
77
78        return $this->row->session_data ?? '';
79    }
80
81    /**
82     * @param string $id
83     * @param string $data
84     *
85     * @return bool
86     */
87    public function write($id, $data): bool
88    {
89        $ip_address   = $this->request->getAttribute('client-ip');
90        $session_time = time();
91        $user_id      = (int) Auth::id();
92
93        if ($this->row === null) {
94            DB::table('session')->insert([
95                'session_id'   => $id,
96                'session_time' => $session_time,
97                'user_id'      => $user_id,
98                'ip_address'   => $ip_address,
99                'session_data' => $data,
100            ]);
101        } else {
102            $updates = [];
103
104            // The user ID can change if we masquerade as another user.
105            if ((int) $this->row->user_id !== $user_id) {
106                $updates['user_id'] = $user_id;
107            }
108
109            if ($this->row->ip_address !== $ip_address) {
110                $updates['ip_address'] = $ip_address;
111            }
112
113            if ($this->row->session_data !== $data) {
114                $updates['session_data'] = $data;
115            }
116
117            if ($session_time - 60 > $this->row->session_time) {
118                $updates['session_time'] = $session_time;
119            }
120
121            if ($updates !== []) {
122                DB::table('session')
123                    ->where('session_id', '=', $id)
124                    ->update($updates);
125            }
126        }
127
128        return true;
129    }
130
131    /**
132     * @param string $id
133     *
134     * @return bool
135     */
136    public function destroy($id): bool
137    {
138        DB::table('session')
139            ->where('session_id', '=', $id)
140            ->delete();
141
142        return true;
143    }
144
145    /**
146     * @param int $max_lifetime
147     *
148     * @return int|false
149     */
150    #[\ReturnTypeWillChange]
151    public function gc($max_lifetime)
152    {
153        return DB::table('session')
154            ->where('session_time', '<', date('Y-m-d H:i:s', time() - $max_lifetime))
155            ->delete();
156    }
157}
158