xref: /webtrees/app/SessionDatabaseHandler.php (revision 5a8afed46297e8105e3e5a33ce37e6a8e88bc79d)
14d7dd147SGreg Roach<?php
23976b470SGreg Roach
34d7dd147SGreg Roach/**
44d7dd147SGreg Roach * webtrees: online genealogy
5d11be702SGreg Roach * Copyright (C) 2023 webtrees development team
64d7dd147SGreg Roach * This program is free software: you can redistribute it and/or modify
74d7dd147SGreg Roach * it under the terms of the GNU General Public License as published by
84d7dd147SGreg Roach * the Free Software Foundation, either version 3 of the License, or
94d7dd147SGreg Roach * (at your option) any later version.
104d7dd147SGreg Roach * This program is distributed in the hope that it will be useful,
114d7dd147SGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of
124d7dd147SGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
134d7dd147SGreg Roach * GNU General Public License for more details.
144d7dd147SGreg Roach * You should have received a copy of the GNU General Public License
1589f7189bSGreg Roach * along with this program. If not, see <https://www.gnu.org/licenses/>.
164d7dd147SGreg Roach */
17fcfa147eSGreg Roach
184d7dd147SGreg Roachdeclare(strict_types=1);
194d7dd147SGreg Roach
204d7dd147SGreg Roachnamespace Fisharebest\Webtrees;
214d7dd147SGreg Roach
224d7dd147SGreg Roachuse Psr\Http\Message\ServerRequestInterface;
234d7dd147SGreg Roachuse SessionHandlerInterface;
244d7dd147SGreg Roach
25f12c148eSGreg Roachuse function date;
26849190baSGreg Roachuse function time;
27f12c148eSGreg Roach
284d7dd147SGreg Roach/**
294d7dd147SGreg Roach * Session handling - stores sessions in the database.
304d7dd147SGreg Roach */
314d7dd147SGreg Roachclass SessionDatabaseHandler implements SessionHandlerInterface
324d7dd147SGreg Roach{
33c4943cffSGreg Roach    private ServerRequestInterface $request;
344d7dd147SGreg Roach
35*f25fc0f9SGreg Roach    private ?object $row = null;
36dfc849d2SGreg Roach
37c4943cffSGreg Roach    /**
38c4943cffSGreg Roach     * @param ServerRequestInterface $request
39c4943cffSGreg Roach     */
403b267247SGreg Roach    public function __construct(ServerRequestInterface $request)
413b267247SGreg Roach    {
424d7dd147SGreg Roach        $this->request = $request;
434d7dd147SGreg Roach    }
444d7dd147SGreg Roach
454d7dd147SGreg Roach    /**
46c4943cffSGreg Roach     * @param string $path
473976b470SGreg Roach     * @param string $name
484d7dd147SGreg Roach     *
494d7dd147SGreg Roach     * @return bool
504d7dd147SGreg Roach     */
5160df7837SGreg Roach    public function open(string $path, string $name): bool
524d7dd147SGreg Roach    {
534d7dd147SGreg Roach        return true;
544d7dd147SGreg Roach    }
554d7dd147SGreg Roach
564d7dd147SGreg Roach    /**
574d7dd147SGreg Roach     * @return bool
584d7dd147SGreg Roach     */
594d7dd147SGreg Roach    public function close(): bool
604d7dd147SGreg Roach    {
614d7dd147SGreg Roach        return true;
624d7dd147SGreg Roach    }
634d7dd147SGreg Roach
644d7dd147SGreg Roach    /**
65c4943cffSGreg Roach     * @param string $id
664d7dd147SGreg Roach     *
674d7dd147SGreg Roach     * @return string
684d7dd147SGreg Roach     */
6960df7837SGreg Roach    public function read(string $id): string
704d7dd147SGreg Roach    {
71dfc849d2SGreg Roach        $this->row = DB::table('session')
72c4943cffSGreg Roach            ->where('session_id', '=', $id)
73dfc849d2SGreg Roach            ->first();
74dfc849d2SGreg Roach
75dfc849d2SGreg Roach        return $this->row->session_data ?? '';
764d7dd147SGreg Roach    }
774d7dd147SGreg Roach
784d7dd147SGreg Roach    /**
79c4943cffSGreg Roach     * @param string $id
80c4943cffSGreg Roach     * @param string $data
814d7dd147SGreg Roach     *
824d7dd147SGreg Roach     * @return bool
834d7dd147SGreg Roach     */
8460df7837SGreg Roach    public function write(string $id, string $data): bool
854d7dd147SGreg Roach    {
86b55cbc6bSGreg Roach        $ip_address = Validator::attributes($this->request)->string('client-ip');
87aadd14baSGreg Roach        $user_id    = (int) Auth::id();
88bfd75698SGreg Roach        $now        = Registry::timestampFactory()->now();
89aadd14baSGreg Roach
90dfc849d2SGreg Roach        if ($this->row === null) {
91dfc849d2SGreg Roach            DB::table('session')->insert([
92c4943cffSGreg Roach                'session_id'   => $id,
93bfd75698SGreg Roach                'session_time' => $now->toDateTimeString(),
94aadd14baSGreg Roach                'user_id'      => $user_id,
95aadd14baSGreg Roach                'ip_address'   => $ip_address,
96c4943cffSGreg Roach                'session_data' => $data,
974d7dd147SGreg Roach            ]);
98dfc849d2SGreg Roach        } else {
99dfc849d2SGreg Roach            $updates = [];
100dfc849d2SGreg Roach
101dfc849d2SGreg Roach            // The user ID can change if we masquerade as another user.
102aadd14baSGreg Roach            if ((int) $this->row->user_id !== $user_id) {
103aadd14baSGreg Roach                $updates['user_id'] = $user_id;
104dfc849d2SGreg Roach            }
105dfc849d2SGreg Roach
106aadd14baSGreg Roach            if ($this->row->ip_address !== $ip_address) {
107aadd14baSGreg Roach                $updates['ip_address'] = $ip_address;
108dfc849d2SGreg Roach            }
109dfc849d2SGreg Roach
110c4943cffSGreg Roach            if ($this->row->session_data !== $data) {
111c4943cffSGreg Roach                $updates['session_data'] = $data;
112dfc849d2SGreg Roach            }
113dfc849d2SGreg Roach
114bfd75698SGreg Roach            // Only update session once a minute to reduce contention on the session table.
115bfd75698SGreg Roach            if ($now->subtractMinutes(1)->timestamp() > Registry::timestampFactory()->fromString($this->row->session_time)->timestamp()) {
116bfd75698SGreg Roach                $updates['session_time'] =  $now->toDateTimeString();
117dfc849d2SGreg Roach            }
118dfc849d2SGreg Roach
119dfc849d2SGreg Roach            if ($updates !== []) {
120dfc849d2SGreg Roach                DB::table('session')
121c4943cffSGreg Roach                    ->where('session_id', '=', $id)
122dfc849d2SGreg Roach                    ->update($updates);
123dfc849d2SGreg Roach            }
124dfc849d2SGreg Roach        }
1254d7dd147SGreg Roach
1264d7dd147SGreg Roach        return true;
1274d7dd147SGreg Roach    }
1284d7dd147SGreg Roach
1294d7dd147SGreg Roach    /**
130c4943cffSGreg Roach     * @param string $id
1314d7dd147SGreg Roach     *
1324d7dd147SGreg Roach     * @return bool
1334d7dd147SGreg Roach     */
13460df7837SGreg Roach    public function destroy(string $id): bool
1354d7dd147SGreg Roach    {
1364d7dd147SGreg Roach        DB::table('session')
137c4943cffSGreg Roach            ->where('session_id', '=', $id)
1384d7dd147SGreg Roach            ->delete();
1394d7dd147SGreg Roach
1404d7dd147SGreg Roach        return true;
1414d7dd147SGreg Roach    }
1424d7dd147SGreg Roach
1434d7dd147SGreg Roach    /**
144c4943cffSGreg Roach     * @param int $max_lifetime
1454d7dd147SGreg Roach     *
14660df7837SGreg Roach     * @return int
1474d7dd147SGreg Roach     */
14860df7837SGreg Roach    public function gc(int $max_lifetime): int
1494d7dd147SGreg Roach    {
15095edec1eSGreg Roach        return DB::table('session')
151f12c148eSGreg Roach            ->where('session_time', '<', date('Y-m-d H:i:s', time() - $max_lifetime))
1524d7dd147SGreg Roach            ->delete();
1534d7dd147SGreg Roach    }
1544d7dd147SGreg Roach}
155