xref: /webtrees/app/Session.php (revision c1010eda29c0909ed4d5d463f32d32bfefdd4dfe)
131bc7874SGreg Roach<?php
231bc7874SGreg Roach/**
331bc7874SGreg Roach * webtrees: online genealogy
41062a142SGreg 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 */
23*c1010edaSGreg Roachclass Session
24*c1010edaSGreg Roach{
2531bc7874SGreg Roach    /**
2631bc7874SGreg Roach     * Start a session
2731bc7874SGreg Roach     *
2831bc7874SGreg Roach     * @param array $config
2931bc7874SGreg Roach     */
30*c1010edaSGreg Roach    public static function start(array $config = [])
31*c1010edaSGreg Roach    {
3213abd6f3SGreg Roach        $default_config = [
339c927afbSGreg Roach            'use_cookies'     => '1',
3431bc7874SGreg Roach            'name'            => 'WT_SESSION',
359c927afbSGreg Roach            'cookie_lifetime' => '0',
369c927afbSGreg Roach            'gc_maxlifetime'  => '7200',
379c927afbSGreg Roach            'gc_probability'  => '1',
389c927afbSGreg Roach            'gc_divisor'      => '100',
3931bc7874SGreg Roach            'cookie_path'     => '',
409c927afbSGreg Roach            'cookie_httponly' => '1',
4113abd6f3SGreg Roach        ];
4231bc7874SGreg Roach        session_register_shutdown();
4331bc7874SGreg Roach        foreach ($config + $default_config as $key => $value) {
443b8c3a1cSGreg Roach            ini_set('session.' . $key, $value);
4531bc7874SGreg Roach        }
4631bc7874SGreg Roach        session_start();
4731bc7874SGreg Roach    }
4831bc7874SGreg Roach
4931bc7874SGreg Roach    /**
5031bc7874SGreg Roach     * Read a value from the session
5131bc7874SGreg Roach     *
5231bc7874SGreg Roach     * @param string $name
5331bc7874SGreg Roach     * @param mixed  $default
5431bc7874SGreg Roach     *
5531bc7874SGreg Roach     * @return mixed
5631bc7874SGreg Roach     */
57*c1010edaSGreg Roach    public static function get($name, $default = null)
58*c1010edaSGreg Roach    {
5963485653SRico Sonntag        return $_SESSION[$name] ?? $default;
6031bc7874SGreg Roach    }
6131bc7874SGreg Roach
6231bc7874SGreg Roach    /**
6331bc7874SGreg Roach     * Write a value to the session
6431bc7874SGreg Roach     *
6531bc7874SGreg Roach     * @param string $name
6631bc7874SGreg Roach     * @param mixed  $value
6731bc7874SGreg Roach     */
68*c1010edaSGreg Roach    public static function put($name, $value)
69*c1010edaSGreg Roach    {
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     */
78*c1010edaSGreg Roach    public static function forget($name)
79*c1010edaSGreg Roach    {
8031bc7874SGreg Roach        unset($_SESSION[$name]);
8131bc7874SGreg Roach    }
8231bc7874SGreg Roach
8331bc7874SGreg Roach    /**
8431bc7874SGreg Roach     * Does a session variable exist?
8531bc7874SGreg Roach     *
8631bc7874SGreg Roach     * @param string $name
8731bc7874SGreg Roach     *
88cbc1590aSGreg Roach     * @return bool
8931bc7874SGreg Roach     */
90*c1010edaSGreg Roach    public static function has($name)
91*c1010edaSGreg Roach    {
9291fb15f0SGreg Roach        return isset($_SESSION[$name]);
9331bc7874SGreg Roach    }
9431bc7874SGreg Roach
9531bc7874SGreg Roach    /**
96f5004097SGreg Roach     * Remove all stored data from the session.
97f5004097SGreg Roach     */
98*c1010edaSGreg Roach    public static function clear()
99*c1010edaSGreg Roach    {
10013abd6f3SGreg Roach        $_SESSION = [];
101f5004097SGreg Roach    }
102f5004097SGreg Roach
103f5004097SGreg Roach    /**
10431bc7874SGreg Roach     * After any change in authentication level, we should use a new session ID.
10531bc7874SGreg Roach     *
10631bc7874SGreg Roach     * @param bool $destroy
10731bc7874SGreg Roach     */
108*c1010edaSGreg Roach    public static function regenerate($destroy = false)
109*c1010edaSGreg Roach    {
110f5004097SGreg Roach        if ($destroy) {
111f5004097SGreg Roach            self::clear();
112f5004097SGreg Roach        }
11331bc7874SGreg Roach        session_regenerate_id($destroy);
11431bc7874SGreg Roach    }
11531bc7874SGreg Roach
11631bc7874SGreg Roach    /**
11731bc7874SGreg Roach     * Set an explicit session ID. Typically used for search robots.
11831bc7874SGreg Roach     *
11931bc7874SGreg Roach     * @param string $id
12031bc7874SGreg Roach     */
121*c1010edaSGreg Roach    public static function setId($id)
122*c1010edaSGreg Roach    {
12331bc7874SGreg Roach        session_id($id);
12431bc7874SGreg Roach    }
12557514a4fSGreg Roach
12657514a4fSGreg Roach    /**
12757514a4fSGreg Roach     * Initialise our session save handler
12857514a4fSGreg Roach     */
129*c1010edaSGreg Roach    public static function setSaveHandler()
130*c1010edaSGreg Roach    {
13157514a4fSGreg Roach        session_set_save_handler(
13257514a4fSGreg Roach            function (): bool {
13357514a4fSGreg Roach                return Session::open();
13457514a4fSGreg Roach            },
13557514a4fSGreg Roach            function (): bool {
13657514a4fSGreg Roach                return Session::close();
13757514a4fSGreg Roach            },
13857514a4fSGreg Roach            function (string $id): string {
13957514a4fSGreg Roach                return Session::read($id);
14057514a4fSGreg Roach            },
14157514a4fSGreg Roach            function (string $id, string $data): bool {
14257514a4fSGreg Roach                return Session::write($id, $data);
14357514a4fSGreg Roach            },
14457514a4fSGreg Roach            function (string $id): bool {
14557514a4fSGreg Roach                return Session::destroy($id);
14657514a4fSGreg Roach            },
14757514a4fSGreg Roach            function (int $maxlifetime): bool {
14857514a4fSGreg Roach                return Session::gc($maxlifetime);
14957514a4fSGreg Roach            }
15057514a4fSGreg Roach        );
15157514a4fSGreg Roach    }
15257514a4fSGreg Roach
15357514a4fSGreg Roach    /**
15457514a4fSGreg Roach     * For session_set_save_handler()
15557514a4fSGreg Roach     *
15657514a4fSGreg Roach     * @return bool
15757514a4fSGreg Roach     */
158*c1010edaSGreg Roach    private static function close()
159*c1010edaSGreg Roach    {
16057514a4fSGreg Roach        return true;
16157514a4fSGreg Roach    }
16257514a4fSGreg Roach
16357514a4fSGreg Roach    /**
16457514a4fSGreg Roach     * For session_set_save_handler()
16557514a4fSGreg Roach     *
16657514a4fSGreg Roach     * @param string $id
16757514a4fSGreg Roach     *
16857514a4fSGreg Roach     * @return bool
16957514a4fSGreg Roach     */
170*c1010edaSGreg Roach    private static function destroy(string $id)
171*c1010edaSGreg Roach    {
17257514a4fSGreg Roach        Database::prepare(
17357514a4fSGreg Roach            "DELETE FROM `##session` WHERE session_id = :session_id"
17457514a4fSGreg Roach        )->execute([
175*c1010edaSGreg Roach            'session_id' => $id,
17657514a4fSGreg Roach        ]);
17757514a4fSGreg Roach
17857514a4fSGreg Roach        return true;
17957514a4fSGreg Roach    }
18057514a4fSGreg Roach
18157514a4fSGreg Roach    /**
18257514a4fSGreg Roach     * For session_set_save_handler()
18357514a4fSGreg Roach     *
18457514a4fSGreg Roach     * @param int $maxlifetime
18557514a4fSGreg Roach     *
18657514a4fSGreg Roach     * @return bool
18757514a4fSGreg Roach     */
188*c1010edaSGreg Roach    private static function gc(int $maxlifetime)
189*c1010edaSGreg Roach    {
19057514a4fSGreg Roach        Database::prepare(
19157514a4fSGreg Roach            "DELETE FROM `##session` WHERE session_time < DATE_SUB(NOW(), INTERVAL :maxlifetime SECOND)"
19257514a4fSGreg Roach        )->execute([
193*c1010edaSGreg Roach            'maxlifetime' => $maxlifetime,
19457514a4fSGreg Roach        ]);
19557514a4fSGreg Roach
19657514a4fSGreg Roach        return true;
19757514a4fSGreg Roach    }
19857514a4fSGreg Roach
19957514a4fSGreg Roach    /**
20057514a4fSGreg Roach     * For session_set_save_handler()
20157514a4fSGreg Roach     *
20257514a4fSGreg Roach     * @return bool
20357514a4fSGreg Roach     */
204*c1010edaSGreg Roach    private static function open()
205*c1010edaSGreg Roach    {
20657514a4fSGreg Roach        return true;
20757514a4fSGreg Roach    }
20857514a4fSGreg Roach
20957514a4fSGreg Roach    /**
21057514a4fSGreg Roach     * For session_set_save_handler()
21157514a4fSGreg Roach     *
21257514a4fSGreg Roach     * @param string $id
21357514a4fSGreg Roach     *
21457514a4fSGreg Roach     * @return string
21557514a4fSGreg Roach     */
216*c1010edaSGreg Roach    private static function read(string $id): string
217*c1010edaSGreg Roach    {
21857514a4fSGreg Roach        return (string)Database::prepare(
21957514a4fSGreg Roach            "SELECT session_data FROM `##session` WHERE session_id = :session_id"
22057514a4fSGreg Roach        )->execute([
221*c1010edaSGreg Roach            'session_id' => $id,
22257514a4fSGreg Roach        ])->fetchOne();
22357514a4fSGreg Roach    }
22457514a4fSGreg Roach
22557514a4fSGreg Roach    /**
22657514a4fSGreg Roach     * For session_set_save_handler()
22757514a4fSGreg Roach     *
22857514a4fSGreg Roach     * @param string $id
22957514a4fSGreg Roach     * @param string $data
23057514a4fSGreg Roach     *
23157514a4fSGreg Roach     * @return bool
23257514a4fSGreg Roach     */
233*c1010edaSGreg Roach    private static function write(string $id, string $data): bool
234*c1010edaSGreg Roach    {
2354c891c40SGreg Roach        $request = Request::createFromGlobals();
2364c891c40SGreg Roach
23757514a4fSGreg Roach        // Only update the session table once per minute, unless the session data has actually changed.
23857514a4fSGreg Roach        Database::prepare(
23957514a4fSGreg Roach            "INSERT INTO `##session` (session_id, user_id, ip_address, session_data, session_time)" .
2404c891c40SGreg Roach            " VALUES (:session_id, :user_id, :ip_address, :data, CURRENT_TIMESTAMP - SECOND(CURRENT_TIMESTAMP))" .
24157514a4fSGreg Roach            " ON DUPLICATE KEY UPDATE" .
24257514a4fSGreg Roach            " user_id      = VALUES(user_id)," .
24357514a4fSGreg Roach            " ip_address   = VALUES(ip_address)," .
24457514a4fSGreg Roach            " session_data = VALUES(session_data)," .
24557514a4fSGreg Roach            " session_time = CURRENT_TIMESTAMP - SECOND(CURRENT_TIMESTAMP)"
24657514a4fSGreg Roach        )->execute([
2474c891c40SGreg Roach            'session_id' => $id,
2484c891c40SGreg Roach            'user_id'    => (int)Auth::id(),
2494c891c40SGreg Roach            'ip_address' => $request->getClientIp(),
2504c891c40SGreg Roach            'data'       => $data,
2514c891c40SGreg Roach        ]);
25257514a4fSGreg Roach
25357514a4fSGreg Roach        return true;
25457514a4fSGreg Roach    }
25531bc7874SGreg Roach}
256