xref: /webtrees/app/Session.php (revision 57514a4ff902f034d7ab7aac5ec98113d86b8f47)
131bc7874SGreg Roach<?php
231bc7874SGreg Roach/**
331bc7874SGreg Roach * webtrees: online genealogy
46bdf7674SGreg Roach * Copyright (C) 2017 webtrees development team
531bc7874SGreg Roach * This program is free software: you can redistribute it and/or modify
631bc7874SGreg Roach * it under the terms of the GNU General Public License as published by
731bc7874SGreg Roach * the Free Software Foundation, either version 3 of the License, or
831bc7874SGreg Roach * (at your option) any later version.
931bc7874SGreg Roach * This program is distributed in the hope that it will be useful,
1031bc7874SGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of
1131bc7874SGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1231bc7874SGreg Roach * GNU General Public License for more details.
1331bc7874SGreg Roach * You should have received a copy of the GNU General Public License
1431bc7874SGreg Roach * along with this program. If not, see <http://www.gnu.org/licenses/>.
1531bc7874SGreg Roach */
1676692c8bSGreg Roachnamespace Fisharebest\Webtrees;
1731bc7874SGreg Roach
1831bc7874SGreg Roach/**
1976692c8bSGreg Roach * Temporary class to migrate to Symfony-based sessions, which need PHP 5.4.
2031bc7874SGreg Roach */
2131bc7874SGreg Roachclass Session {
2231bc7874SGreg Roach	/**
2331bc7874SGreg Roach	 * Start a session
2431bc7874SGreg Roach	 *
2531bc7874SGreg Roach	 * @param array $config
2631bc7874SGreg Roach	 */
2713abd6f3SGreg Roach	public static function start(array $config = []) {
2813abd6f3SGreg Roach		$default_config = [
299c927afbSGreg Roach			'use_cookies'     => '1',
3031bc7874SGreg Roach			'name'            => 'WT_SESSION',
319c927afbSGreg Roach			'cookie_lifetime' => '0',
329c927afbSGreg Roach			'gc_maxlifetime'  => '7200',
339c927afbSGreg Roach			'gc_probability'  => '1',
349c927afbSGreg Roach			'gc_divisor'      => '100',
3531bc7874SGreg Roach			'cookie_path'     => '',
369c927afbSGreg Roach			'cookie_httponly' => '1',
3713abd6f3SGreg Roach		];
3831bc7874SGreg Roach		session_register_shutdown();
3931bc7874SGreg Roach		foreach ($config + $default_config as $key => $value) {
403b8c3a1cSGreg Roach			ini_set('session.' . $key, $value);
4131bc7874SGreg Roach		}
4231bc7874SGreg Roach		session_start();
4331bc7874SGreg Roach	}
4431bc7874SGreg Roach
4531bc7874SGreg Roach	/**
4631bc7874SGreg Roach	 * Read a value from the session
4731bc7874SGreg Roach	 *
4831bc7874SGreg Roach	 * @param string $name
4931bc7874SGreg Roach	 * @param mixed  $default
5031bc7874SGreg Roach	 *
5131bc7874SGreg Roach	 * @return mixed
5231bc7874SGreg Roach	 */
5331bc7874SGreg Roach	public static function get($name, $default = null) {
5431bc7874SGreg Roach		if (isset($_SESSION[$name])) {
5531bc7874SGreg Roach			return $_SESSION[$name];
5631bc7874SGreg Roach		} else {
5731bc7874SGreg Roach			return $default;
5831bc7874SGreg Roach		}
5931bc7874SGreg Roach	}
6031bc7874SGreg Roach
6131bc7874SGreg Roach	/**
6231bc7874SGreg Roach	 * Write a value to the session
6331bc7874SGreg Roach	 *
6431bc7874SGreg Roach	 * @param string $name
6531bc7874SGreg Roach	 * @param mixed  $value
6631bc7874SGreg Roach	 */
6731bc7874SGreg Roach	public static function put($name, $value) {
6831bc7874SGreg Roach		$_SESSION[$name] = $value;
6931bc7874SGreg Roach	}
7031bc7874SGreg Roach
7131bc7874SGreg Roach	/**
7231bc7874SGreg Roach	 * Remove a value from the session
7331bc7874SGreg Roach	 *
7431bc7874SGreg Roach	 * @param string $name
7531bc7874SGreg Roach	 */
7631bc7874SGreg Roach	public static function forget($name) {
7731bc7874SGreg Roach		unset($_SESSION[$name]);
7831bc7874SGreg Roach	}
7931bc7874SGreg Roach
8031bc7874SGreg Roach	/**
8131bc7874SGreg Roach	 * Does a session variable exist?
8231bc7874SGreg Roach	 *
8331bc7874SGreg Roach	 * @param string $name
8431bc7874SGreg Roach	 *
85cbc1590aSGreg Roach	 * @return bool
8631bc7874SGreg Roach	 */
8731bc7874SGreg Roach	public static function has($name) {
8891fb15f0SGreg Roach		return isset($_SESSION[$name]);
8931bc7874SGreg Roach	}
9031bc7874SGreg Roach
9131bc7874SGreg Roach	/**
92f5004097SGreg Roach	 * Remove all stored data from the session.
93f5004097SGreg Roach	 */
94f5004097SGreg Roach	public static function clear() {
9513abd6f3SGreg Roach		$_SESSION = [];
96f5004097SGreg Roach	}
97f5004097SGreg Roach
98f5004097SGreg Roach	/**
9931bc7874SGreg Roach	 * After any change in authentication level, we should use a new session ID.
10031bc7874SGreg Roach	 *
10131bc7874SGreg Roach	 * @param bool $destroy
10231bc7874SGreg Roach	 */
10331bc7874SGreg Roach	public static function regenerate($destroy = false) {
104f5004097SGreg Roach		if ($destroy) {
105f5004097SGreg Roach			self::clear();
106f5004097SGreg Roach		}
10731bc7874SGreg Roach		session_regenerate_id($destroy);
10831bc7874SGreg Roach	}
10931bc7874SGreg Roach
11031bc7874SGreg Roach	/**
11131bc7874SGreg Roach	 * Set an explicit session ID. Typically used for search robots.
11231bc7874SGreg Roach	 *
11331bc7874SGreg Roach	 * @param string $id
11431bc7874SGreg Roach	 */
11531bc7874SGreg Roach	public static function setId($id) {
11631bc7874SGreg Roach		session_id($id);
11731bc7874SGreg Roach	}
118*57514a4fSGreg Roach
119*57514a4fSGreg Roach	/**
120*57514a4fSGreg Roach	 * Initialise our session save handler
121*57514a4fSGreg Roach	 */
122*57514a4fSGreg Roach	public static function setSaveHandler() {
123*57514a4fSGreg Roach		session_set_save_handler(
124*57514a4fSGreg Roach			function (): bool {
125*57514a4fSGreg Roach				return Session::open();
126*57514a4fSGreg Roach			},
127*57514a4fSGreg Roach			function ():bool {
128*57514a4fSGreg Roach				return Session::close();
129*57514a4fSGreg Roach			},
130*57514a4fSGreg Roach			function (string $id): string {
131*57514a4fSGreg Roach				return Session::read($id);
132*57514a4fSGreg Roach			},
133*57514a4fSGreg Roach			function (string $id, string $data): bool {
134*57514a4fSGreg Roach				return Session::write($id, $data);
135*57514a4fSGreg Roach			},
136*57514a4fSGreg Roach			function (string $id): bool {
137*57514a4fSGreg Roach				return Session::destroy($id);
138*57514a4fSGreg Roach			},
139*57514a4fSGreg Roach			function (int $maxlifetime):bool {
140*57514a4fSGreg Roach				return Session::gc($maxlifetime);
141*57514a4fSGreg Roach			}
142*57514a4fSGreg Roach		);
143*57514a4fSGreg Roach	}
144*57514a4fSGreg Roach
145*57514a4fSGreg Roach	/**
146*57514a4fSGreg Roach	 * For session_set_save_handler()
147*57514a4fSGreg Roach	 *
148*57514a4fSGreg Roach	 * @return bool
149*57514a4fSGreg Roach	 */
150*57514a4fSGreg Roach	private static function close() {
151*57514a4fSGreg Roach		return true;
152*57514a4fSGreg Roach	}
153*57514a4fSGreg Roach
154*57514a4fSGreg Roach	/**
155*57514a4fSGreg Roach	 * For session_set_save_handler()
156*57514a4fSGreg Roach	 *
157*57514a4fSGreg Roach	 * @param string $id
158*57514a4fSGreg Roach	 *
159*57514a4fSGreg Roach	 * @return bool
160*57514a4fSGreg Roach	 */
161*57514a4fSGreg Roach	private static function destroy(string $id) {
162*57514a4fSGreg Roach		Database::prepare(
163*57514a4fSGreg Roach			"DELETE FROM `##session` WHERE session_id = :session_id"
164*57514a4fSGreg Roach		)->execute([
165*57514a4fSGreg Roach			'session_id' => $id
166*57514a4fSGreg Roach		]);
167*57514a4fSGreg Roach
168*57514a4fSGreg Roach		return true;
169*57514a4fSGreg Roach	}
170*57514a4fSGreg Roach
171*57514a4fSGreg Roach	/**
172*57514a4fSGreg Roach	 * For session_set_save_handler()
173*57514a4fSGreg Roach	 *
174*57514a4fSGreg Roach	 * @param int $maxlifetime
175*57514a4fSGreg Roach	 *
176*57514a4fSGreg Roach	 * @return bool
177*57514a4fSGreg Roach	 */
178*57514a4fSGreg Roach	private static function gc(int $maxlifetime) {
179*57514a4fSGreg Roach		Database::prepare(
180*57514a4fSGreg Roach			"DELETE FROM `##session` WHERE session_time < DATE_SUB(NOW(), INTERVAL :maxlifetime SECOND)"
181*57514a4fSGreg Roach		)->execute([
182*57514a4fSGreg Roach			'maxlifetime' => $maxlifetime
183*57514a4fSGreg Roach		]);
184*57514a4fSGreg Roach
185*57514a4fSGreg Roach		return true;
186*57514a4fSGreg Roach	}
187*57514a4fSGreg Roach
188*57514a4fSGreg Roach	/**
189*57514a4fSGreg Roach	 * For session_set_save_handler()
190*57514a4fSGreg Roach	 *
191*57514a4fSGreg Roach	 * @return bool
192*57514a4fSGreg Roach	 */
193*57514a4fSGreg Roach	private static function open() {
194*57514a4fSGreg Roach		return true;
195*57514a4fSGreg Roach	}
196*57514a4fSGreg Roach
197*57514a4fSGreg Roach	/**
198*57514a4fSGreg Roach	 * For session_set_save_handler()
199*57514a4fSGreg Roach	 *
200*57514a4fSGreg Roach	 * @param string $id
201*57514a4fSGreg Roach	 *
202*57514a4fSGreg Roach	 * @return string
203*57514a4fSGreg Roach	 */
204*57514a4fSGreg Roach	private static function read(string $id): string {
205*57514a4fSGreg Roach		return (string) Database::prepare(
206*57514a4fSGreg Roach			"SELECT session_data FROM `##session` WHERE session_id = :session_id"
207*57514a4fSGreg Roach		)->execute([
208*57514a4fSGreg Roach			'session_id' => $id
209*57514a4fSGreg Roach		])->fetchOne();
210*57514a4fSGreg Roach	}
211*57514a4fSGreg Roach
212*57514a4fSGreg Roach	/**
213*57514a4fSGreg Roach	 * For session_set_save_handler()
214*57514a4fSGreg Roach	 *
215*57514a4fSGreg Roach	 * @param string $id
216*57514a4fSGreg Roach	 * @param string $data
217*57514a4fSGreg Roach	 *
218*57514a4fSGreg Roach	 * @return bool
219*57514a4fSGreg Roach	 */
220*57514a4fSGreg Roach	private static function write(string $id, string $data): bool {
221*57514a4fSGreg Roach		// Only update the session table once per minute, unless the session data has actually changed.
222*57514a4fSGreg Roach		Database::prepare(
223*57514a4fSGreg Roach			"INSERT INTO `##session` (session_id, user_id, ip_address, session_data, session_time)" .
224*57514a4fSGreg Roach			" VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP - SECOND(CURRENT_TIMESTAMP))" .
225*57514a4fSGreg Roach			" ON DUPLICATE KEY UPDATE" .
226*57514a4fSGreg Roach			" user_id      = VALUES(user_id)," .
227*57514a4fSGreg Roach			" ip_address   = VALUES(ip_address)," .
228*57514a4fSGreg Roach			" session_data = VALUES(session_data)," .
229*57514a4fSGreg Roach			" session_time = CURRENT_TIMESTAMP - SECOND(CURRENT_TIMESTAMP)"
230*57514a4fSGreg Roach		)->execute([
231*57514a4fSGreg Roach			$id,
232*57514a4fSGreg Roach			(int) Auth::id(),
233*57514a4fSGreg Roach			WT_CLIENT_IP,
234*57514a4fSGreg Roach			$data]
235*57514a4fSGreg Roach		);
236*57514a4fSGreg Roach
237*57514a4fSGreg Roach		return true;
238*57514a4fSGreg Roach	}
23931bc7874SGreg Roach}
240