xref: /webtrees/app/Session.php (revision 1062a1429914c995339f502856821457aa975a5a)
131bc7874SGreg Roach<?php
231bc7874SGreg Roach/**
331bc7874SGreg Roach * webtrees: online genealogy
4*1062a142SGreg Roach * Copyright (C) 2018 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
184c891c40SGreg Roachuse Symfony\Component\HttpFoundation\Request;
194c891c40SGreg Roach
2031bc7874SGreg Roach/**
2142af74e7SGreg Roach * Session handling
2231bc7874SGreg Roach */
2331bc7874SGreg Roachclass Session {
2431bc7874SGreg Roach	/**
2531bc7874SGreg Roach	 * Start a session
2631bc7874SGreg Roach	 *
2731bc7874SGreg Roach	 * @param array $config
2831bc7874SGreg Roach	 */
2913abd6f3SGreg Roach	public static function start(array $config = []) {
3013abd6f3SGreg Roach		$default_config = [
319c927afbSGreg Roach			'use_cookies'     => '1',
3231bc7874SGreg Roach			'name'            => 'WT_SESSION',
339c927afbSGreg Roach			'cookie_lifetime' => '0',
349c927afbSGreg Roach			'gc_maxlifetime'  => '7200',
359c927afbSGreg Roach			'gc_probability'  => '1',
369c927afbSGreg Roach			'gc_divisor'      => '100',
3731bc7874SGreg Roach			'cookie_path'     => '',
389c927afbSGreg Roach			'cookie_httponly' => '1',
3913abd6f3SGreg Roach		];
4031bc7874SGreg Roach		session_register_shutdown();
4131bc7874SGreg Roach		foreach ($config + $default_config as $key => $value) {
423b8c3a1cSGreg Roach			ini_set('session.' . $key, $value);
4331bc7874SGreg Roach		}
4431bc7874SGreg Roach		session_start();
4531bc7874SGreg Roach	}
4631bc7874SGreg Roach
4731bc7874SGreg Roach	/**
4831bc7874SGreg Roach	 * Read a value from the session
4931bc7874SGreg Roach	 *
5031bc7874SGreg Roach	 * @param string $name
5131bc7874SGreg Roach	 * @param mixed  $default
5231bc7874SGreg Roach	 *
5331bc7874SGreg Roach	 * @return mixed
5431bc7874SGreg Roach	 */
5531bc7874SGreg Roach	public static function get($name, $default = null) {
5631bc7874SGreg Roach		if (isset($_SESSION[$name])) {
5731bc7874SGreg Roach			return $_SESSION[$name];
5831bc7874SGreg Roach		} else {
5931bc7874SGreg Roach			return $default;
6031bc7874SGreg Roach		}
6131bc7874SGreg Roach	}
6231bc7874SGreg Roach
6331bc7874SGreg Roach	/**
6431bc7874SGreg Roach	 * Write a value to the session
6531bc7874SGreg Roach	 *
6631bc7874SGreg Roach	 * @param string $name
6731bc7874SGreg Roach	 * @param mixed  $value
6831bc7874SGreg Roach	 */
6931bc7874SGreg Roach	public static function put($name, $value) {
7031bc7874SGreg Roach		$_SESSION[$name] = $value;
7131bc7874SGreg Roach	}
7231bc7874SGreg Roach
7331bc7874SGreg Roach	/**
7431bc7874SGreg Roach	 * Remove a value from the session
7531bc7874SGreg Roach	 *
7631bc7874SGreg Roach	 * @param string $name
7731bc7874SGreg Roach	 */
7831bc7874SGreg Roach	public static function forget($name) {
7931bc7874SGreg Roach		unset($_SESSION[$name]);
8031bc7874SGreg Roach	}
8131bc7874SGreg Roach
8231bc7874SGreg Roach	/**
8331bc7874SGreg Roach	 * Does a session variable exist?
8431bc7874SGreg Roach	 *
8531bc7874SGreg Roach	 * @param string $name
8631bc7874SGreg Roach	 *
87cbc1590aSGreg Roach	 * @return bool
8831bc7874SGreg Roach	 */
8931bc7874SGreg Roach	public static function has($name) {
9091fb15f0SGreg Roach		return isset($_SESSION[$name]);
9131bc7874SGreg Roach	}
9231bc7874SGreg Roach
9331bc7874SGreg Roach	/**
94f5004097SGreg Roach	 * Remove all stored data from the session.
95f5004097SGreg Roach	 */
96f5004097SGreg Roach	public static function clear() {
9713abd6f3SGreg Roach		$_SESSION = [];
98f5004097SGreg Roach	}
99f5004097SGreg Roach
100f5004097SGreg Roach	/**
10131bc7874SGreg Roach	 * After any change in authentication level, we should use a new session ID.
10231bc7874SGreg Roach	 *
10331bc7874SGreg Roach	 * @param bool $destroy
10431bc7874SGreg Roach	 */
10531bc7874SGreg Roach	public static function regenerate($destroy = false) {
106f5004097SGreg Roach		if ($destroy) {
107f5004097SGreg Roach			self::clear();
108f5004097SGreg Roach		}
10931bc7874SGreg Roach		session_regenerate_id($destroy);
11031bc7874SGreg Roach	}
11131bc7874SGreg Roach
11231bc7874SGreg Roach	/**
11331bc7874SGreg Roach	 * Set an explicit session ID. Typically used for search robots.
11431bc7874SGreg Roach	 *
11531bc7874SGreg Roach	 * @param string $id
11631bc7874SGreg Roach	 */
11731bc7874SGreg Roach	public static function setId($id) {
11831bc7874SGreg Roach		session_id($id);
11931bc7874SGreg Roach	}
12057514a4fSGreg Roach
12157514a4fSGreg Roach	/**
12257514a4fSGreg Roach	 * Initialise our session save handler
12357514a4fSGreg Roach	 */
12457514a4fSGreg Roach	public static function setSaveHandler() {
12557514a4fSGreg Roach		session_set_save_handler(
12657514a4fSGreg Roach			function (): bool {
12757514a4fSGreg Roach				return Session::open();
12857514a4fSGreg Roach			},
12957514a4fSGreg Roach			function ():bool {
13057514a4fSGreg Roach				return Session::close();
13157514a4fSGreg Roach			},
13257514a4fSGreg Roach			function (string $id): string {
13357514a4fSGreg Roach				return Session::read($id);
13457514a4fSGreg Roach			},
13557514a4fSGreg Roach			function (string $id, string $data): bool {
13657514a4fSGreg Roach				return Session::write($id, $data);
13757514a4fSGreg Roach			},
13857514a4fSGreg Roach			function (string $id): bool {
13957514a4fSGreg Roach				return Session::destroy($id);
14057514a4fSGreg Roach			},
14157514a4fSGreg Roach			function (int $maxlifetime):bool {
14257514a4fSGreg Roach				return Session::gc($maxlifetime);
14357514a4fSGreg Roach			}
14457514a4fSGreg Roach		);
14557514a4fSGreg Roach	}
14657514a4fSGreg Roach
14757514a4fSGreg Roach	/**
14857514a4fSGreg Roach	 * For session_set_save_handler()
14957514a4fSGreg Roach	 *
15057514a4fSGreg Roach	 * @return bool
15157514a4fSGreg Roach	 */
15257514a4fSGreg Roach	private static function close() {
15357514a4fSGreg Roach		return true;
15457514a4fSGreg Roach	}
15557514a4fSGreg Roach
15657514a4fSGreg Roach	/**
15757514a4fSGreg Roach	 * For session_set_save_handler()
15857514a4fSGreg Roach	 *
15957514a4fSGreg Roach	 * @param string $id
16057514a4fSGreg Roach	 *
16157514a4fSGreg Roach	 * @return bool
16257514a4fSGreg Roach	 */
16357514a4fSGreg Roach	private static function destroy(string $id) {
16457514a4fSGreg Roach		Database::prepare(
16557514a4fSGreg Roach			"DELETE FROM `##session` WHERE session_id = :session_id"
16657514a4fSGreg Roach		)->execute([
16757514a4fSGreg Roach			'session_id' => $id
16857514a4fSGreg Roach		]);
16957514a4fSGreg Roach
17057514a4fSGreg Roach		return true;
17157514a4fSGreg Roach	}
17257514a4fSGreg Roach
17357514a4fSGreg Roach	/**
17457514a4fSGreg Roach	 * For session_set_save_handler()
17557514a4fSGreg Roach	 *
17657514a4fSGreg Roach	 * @param int $maxlifetime
17757514a4fSGreg Roach	 *
17857514a4fSGreg Roach	 * @return bool
17957514a4fSGreg Roach	 */
18057514a4fSGreg Roach	private static function gc(int $maxlifetime) {
18157514a4fSGreg Roach		Database::prepare(
18257514a4fSGreg Roach			"DELETE FROM `##session` WHERE session_time < DATE_SUB(NOW(), INTERVAL :maxlifetime SECOND)"
18357514a4fSGreg Roach		)->execute([
18457514a4fSGreg Roach			'maxlifetime' => $maxlifetime
18557514a4fSGreg Roach		]);
18657514a4fSGreg Roach
18757514a4fSGreg Roach		return true;
18857514a4fSGreg Roach	}
18957514a4fSGreg Roach
19057514a4fSGreg Roach	/**
19157514a4fSGreg Roach	 * For session_set_save_handler()
19257514a4fSGreg Roach	 *
19357514a4fSGreg Roach	 * @return bool
19457514a4fSGreg Roach	 */
19557514a4fSGreg Roach	private static function open() {
19657514a4fSGreg Roach		return true;
19757514a4fSGreg Roach	}
19857514a4fSGreg Roach
19957514a4fSGreg Roach	/**
20057514a4fSGreg Roach	 * For session_set_save_handler()
20157514a4fSGreg Roach	 *
20257514a4fSGreg Roach	 * @param string $id
20357514a4fSGreg Roach	 *
20457514a4fSGreg Roach	 * @return string
20557514a4fSGreg Roach	 */
20657514a4fSGreg Roach	private static function read(string $id): string {
20757514a4fSGreg Roach		return (string) Database::prepare(
20857514a4fSGreg Roach			"SELECT session_data FROM `##session` WHERE session_id = :session_id"
20957514a4fSGreg Roach		)->execute([
21057514a4fSGreg Roach			'session_id' => $id
21157514a4fSGreg Roach		])->fetchOne();
21257514a4fSGreg Roach	}
21357514a4fSGreg Roach
21457514a4fSGreg Roach	/**
21557514a4fSGreg Roach	 * For session_set_save_handler()
21657514a4fSGreg Roach	 *
21757514a4fSGreg Roach	 * @param string $id
21857514a4fSGreg Roach	 * @param string $data
21957514a4fSGreg Roach	 *
22057514a4fSGreg Roach	 * @return bool
22157514a4fSGreg Roach	 */
22257514a4fSGreg Roach	private static function write(string $id, string $data): bool {
2234c891c40SGreg Roach		$request = Request::createFromGlobals();
2244c891c40SGreg Roach
22557514a4fSGreg Roach		// Only update the session table once per minute, unless the session data has actually changed.
22657514a4fSGreg Roach		Database::prepare(
22757514a4fSGreg Roach			"INSERT INTO `##session` (session_id, user_id, ip_address, session_data, session_time)" .
2284c891c40SGreg Roach			" VALUES (:session_id, :user_id, :ip_address, :data, CURRENT_TIMESTAMP - SECOND(CURRENT_TIMESTAMP))" .
22957514a4fSGreg Roach			" ON DUPLICATE KEY UPDATE" .
23057514a4fSGreg Roach			" user_id      = VALUES(user_id)," .
23157514a4fSGreg Roach			" ip_address   = VALUES(ip_address)," .
23257514a4fSGreg Roach			" session_data = VALUES(session_data)," .
23357514a4fSGreg Roach			" session_time = CURRENT_TIMESTAMP - SECOND(CURRENT_TIMESTAMP)"
23457514a4fSGreg Roach		)->execute([
2354c891c40SGreg Roach			'session_id' => $id,
2364c891c40SGreg Roach			'user_id'    => (int) Auth::id(),
2374c891c40SGreg Roach			'ip_address' => $request->getClientIp(),
2384c891c40SGreg Roach			'data'       => $data,
2394c891c40SGreg Roach		]);
24057514a4fSGreg Roach
24157514a4fSGreg Roach		return true;
24257514a4fSGreg Roach	}
24331bc7874SGreg Roach}
244