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