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 * @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(string $path, string $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(string $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(string $id, string $data): bool 87 { 88 $ip_address = Validator::attributes($this->request)->string('client-ip'); 89 $session_time = time(); 90 $user_id = (int) Auth::id(); 91 92 if ($this->row === null) { 93 DB::table('session')->insert([ 94 'session_id' => $id, 95 'session_time' => date('Y-m-d H:i:s', $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 - 60 > $this->row->session_time) { 117 $updates['session_time'] = date('Y-m-d H:i:s', $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(string $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 148 */ 149 public function gc(int $max_lifetime): int 150 { 151 return DB::table('session') 152 ->where('session_time', '<', date('Y-m-d H:i:s', time() - $max_lifetime)) 153 ->delete(); 154 } 155} 156