xref: /webtrees/app/SessionDatabaseHandler.php (revision d11be7027e34e3121be11cc025421873364403f9)
1<?php
2
3/**
4 * webtrees: online genealogy
5 * Copyright (C) 2023 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;
27use function time;
28
29/**
30 * Session handling - stores sessions in the database.
31 */
32class SessionDatabaseHandler implements SessionHandlerInterface
33{
34    private ServerRequestInterface $request;
35
36    private ?object $row;
37
38    /**
39     * SessionDatabaseHandler constructor.
40     *
41     * @param ServerRequestInterface $request
42     */
43    public function __construct(ServerRequestInterface $request)
44    {
45        $this->request = $request;
46    }
47
48    /**
49     * @param string $path
50     * @param string $name
51     *
52     * @return bool
53     */
54    public function open(string $path, string $name): bool
55    {
56        return true;
57    }
58
59    /**
60     * @return bool
61     */
62    public function close(): bool
63    {
64        return true;
65    }
66
67    /**
68     * @param string $id
69     *
70     * @return string
71     */
72    public function read(string $id): string
73    {
74        $this->row = DB::table('session')
75            ->where('session_id', '=', $id)
76            ->first();
77
78
79        return $this->row->session_data ?? '';
80    }
81
82    /**
83     * @param string $id
84     * @param string $data
85     *
86     * @return bool
87     */
88    public function write(string $id, string $data): bool
89    {
90        $ip_address   = Validator::attributes($this->request)->string('client-ip');
91        $session_time = time();
92        $user_id      = (int) Auth::id();
93
94        if ($this->row === null) {
95            DB::table('session')->insert([
96                'session_id'   => $id,
97                'session_time' => date('Y-m-d H:i:s', $session_time),
98                'user_id'      => $user_id,
99                'ip_address'   => $ip_address,
100                'session_data' => $data,
101            ]);
102        } else {
103            $updates = [];
104
105            // The user ID can change if we masquerade as another user.
106            if ((int) $this->row->user_id !== $user_id) {
107                $updates['user_id'] = $user_id;
108            }
109
110            if ($this->row->ip_address !== $ip_address) {
111                $updates['ip_address'] = $ip_address;
112            }
113
114            if ($this->row->session_data !== $data) {
115                $updates['session_data'] = $data;
116            }
117
118            if ($session_time - 60 > $this->row->session_time) {
119                $updates['session_time'] = date('Y-m-d H:i:s', $session_time);
120            }
121
122            if ($updates !== []) {
123                DB::table('session')
124                    ->where('session_id', '=', $id)
125                    ->update($updates);
126            }
127        }
128
129        return true;
130    }
131
132    /**
133     * @param string $id
134     *
135     * @return bool
136     */
137    public function destroy(string $id): bool
138    {
139        DB::table('session')
140            ->where('session_id', '=', $id)
141            ->delete();
142
143        return true;
144    }
145
146    /**
147     * @param int $max_lifetime
148     *
149     * @return int
150     */
151    public function gc(int $max_lifetime): int
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