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