yf_session class

Core session handler.

Fileplugins/session/classes/yf_session.class.php
Use_class('session')

Public properties

A site sets them in its config with $PROJECT_CONF['session']['NAME'] = ...;

PropertyDefaultDescription
$CUSTOM_HANDLER
string
'' Custom handler name
$DESTROY_EXPIRED
string
false
$DOMAIN
string
''
$LIFE_TIME
int
18000 Session life time (in seconds)
$NAME
string
'' Current session name
$OFF
bool
false Disable sessions or not
$REFERER_CHECK
string
''
$SAVE_DIR
string
'' Custom session save dir (leave ampty to skip), example: 'session_data/'
$USE_STRICT_MODE
bool
false SECURITY: session.use_strict_mode, PHP refuses session ids it did not create (stops session fixation); default false keeps the previous ini value, 4.0 flips
$USE_UNIQUE_NAME
string
true Custom session name
$_driver
bool
null @conf_skip
$_started
bool
false @conf_skip

Public methods 6

start

public function start()
    public function start()
    {
        $main = main();
        if ( ! empty($this->_started) || $main->is_console() || conf('SESSION_OFF') || $this->OFF) {
            return false;
        }
        // Set custom session name
        if ($this->USE_UNIQUE_NAME) {
            $force_name_path = conf('_SESSION_FORCE_NAME_PATH');
            $_name_path = isset($force_name_path) ? $force_name_path : (MAIN_TYPE_ADMIN ? ADMIN_SITE_PATH : SITE_PATH);
            $_name_path = str_replace('\\', '/', OS_WINDOWS ? strtolower($_name_path) : $_name_path);
            $this->NAME = 'sid_' . (int) (abs(crc32($_name_path)));
            session_name($this->NAME);
        } else {
            $this->NAME = ini_get('session.name'); // Usually PHPSESSID
        }
        $main->SESSION_NAME = $this->NAME;
        if (session_id() !== '') { // (session_status() == PHP_SESSION_ACTIVE) => PHP 5.4+ only
            return true;
        }
        if (ini_get('session.use_trans_sid')) {
            @ini_set('session.use_trans_sid', 0); // We need @ here to avoid error when session already started
        }
        ini_set('url_rewriter.tags', '');
        if ( ! empty($this->LIFE_TIME)) {
            ini_set('session.cookie_lifetime', $this->LIFE_TIME);
            ini_set('session.gc_probability', 0);
            ini_set('session.gc_maxlifetime', $this->LIFE_TIME);
        }
        ini_set('session.use_cookies', 1);
        if ( ! ini_get('session.use_only_cookies')) {
            ini_set('session.use_only_cookies', 1);
        }
        if ($this->COOKIE_PATH) {
            ini_set('session.cookie_path', $this->COOKIE_PATH);
        }
        if ($this->DOMAIN) {
            ini_set('session.cookie_domain', $this->DOMAIN);
        }
        if ($this->COOKIE_SECURE) {
            ini_set('session.cookie_secure', 1);
        }
        if ($this->COOKIE_HTTPONLY) {
            ini_set('session.cookie_httponly', 1);
        }
        if ($this->USE_STRICT_MODE) {
            ini_set('session.use_strict_mode', 1);
        }
        if ($this->REFERER_CHECK) { // WEB_PATH
            ini_set('session.referer_check', $this->REFERER_CHECK);
        }
        conf('COOKIES_ENABLED', ($_COOKIE[$this->NAME] ?? null) !== null ? 1 : 0);
        // Check if we have valid session name
        if (($_COOKIE[$this->NAME] ?? null) !== null) {
            $_test_result = preg_replace('/[^a-z0-9]/i', '', $_COOKIE[$this->NAME]);
            if ($_test_result !== ($_COOKIE[$this->NAME] ?? null)) {
                $_COOKIE[$this->NAME] = abs(crc32(microtime(true)));
                session_id(($_COOKIE[$this->NAME] ?? null));
            }
        }
        if ( ! empty($this->SAVE_DIR)) {
            $s_path = STORAGE_PATH . $this->SAVE_DIR;
            if ( ! file_exists($s_path)) {
                mkdir($s_path, 0755, true);
            }
            session_save_path($s_path);
        }
        if ($this->CUSTOM_HANDLER && $this->_driver_setup($this->CUSTOM_HANDLER)) {
            $main->session = $this->_driver;
        }
        // cross-site: 'None' needs Secure, so it only applies over HTTPS; 'Lax'/'Strict' work on both
        $samesite = $this->_samesite($this->COOKIE_SAMESITE);
        if (is_https()) {
            session_set_cookie_params(['SameSite' => $samesite, 'Secure' => true]);
        } elseif ($samesite !== 'None') {
            session_set_cookie_params(['SameSite' => $samesite]);
        }
        session_start();
        // Instruct bots to totally ignore current page
        if (DEBUG_MODE || MAIN_TYPE_ADMIN || conf('ROBOTS_NO_INDEX')) {
            header('X-Robots-Tag: noindex,nofollow,noarchive,nosnippet');
        }
        $now = time();
        $last_update = ($_SESSION['last_update'] ?? null);
        if ($last_update) {
            $diff = $now - $last_update;
            $percent = $diff / $this->LIFE_TIME * 100;
            // Session expired
            if ($percent > 100 && $this->DESTROY_EXPIRED) {
                session_regenerate_id($destroy = true);
                // Session need to be regenerated
            } elseif ($percent > 10) {
                session_regenerate_id();
                $_SESSION['last_update'] = $now;
            }
        } else {
            $_SESSION['last_update'] = $now;
        }
        $_SESSION['sys_main_type'] = MAIN_TYPE;
        @$_SESSION['sys_requests_count']++;
        $_SESSION['sys_user_ip'] = common()->get_ip();
        $_SESSION['sys_user_geo_country'] = $_SERVER['GEOIP_COUNTRY_CODE'] ?? '';
        $_SESSION['sys_user_current_url'] = (is_https() ? 'https://' : 'http://') . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
        $main->_session_init_complete = true;
        $this->_started = true;
        return $this->_started;
    }

stop

public function stop()
    public function stop()
    {
        session_write_close();
        return true;
    }

Methods starting with _ hooks and helpers; not routable as page actions

__call

public function __call($name, $args)

Catch missing method call.

    public function __call($name, $args)
    {
        return main()->extend_call($this, $name, $args);
    }

_driver_setup

public function _driver_setup($driver_name)
    public function _driver_setup($driver_name)
    {
        $session_class_name = 'session_driver_' . $driver_name;
        $session_loaded_class_name = main()->load_class_file($session_class_name, 'classes/session/');
        if (empty($session_loaded_class_name)) {
            return false;
        }
        $this->_driver = new $session_loaded_class_name();
        main()->set_module_conf($session_class_name, $this->_driver);
        // PHP 8.4 deprecates the callable form of session_set_save_handler(); the adapter keeps every driver (including project ones) working unchanged
        require_once __DIR__ . '/session/yf_session_handler_adapter.class.php';
        session_set_save_handler(new yf_session_handler_adapter($this->_driver), true);
        return $this->_driver;
    }

_init

public function _init()
    public function _init()
    {
        $main = main();
        // Import session settings from main class, but prefixed with "SESSION_"
        $prefix = 'SESSION_';
        $plen = strlen($prefix);
        foreach (get_object_vars($main) as $k => $v) {
            if (substr($k, 0, $plen) !== $prefix) {
                continue;
            }
            $name = substr($k, $plen);
            $this->$name = $v;
        }
    }

_samesite

public function _samesite($value)

'None', 'Lax' or 'Strict' in any case; anything else falls back to 'Lax' rather than sending a bad attribute.

    public function _samesite($value)
    {
        $value = ucfirst(strtolower(trim((string) $value)));
        return in_array($value, ['None', 'Lax', 'Strict'], true) ? $value : 'Lax';
    }