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