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