dadajuice/zephyrus

View on GitHub
src/Zephyrus/Core/Session.php

Summary

Maintainability
A
0 mins
Test Coverage
<?php namespace Zephyrus\Core;

use Zephyrus\Core\Session\SessionConfiguration;
use Zephyrus\Core\Session\SessionFingerprintManager;
use Zephyrus\Core\Session\SessionIdentifierManager;
use Zephyrus\Exceptions\Session\SessionDatabaseStructureException;
use Zephyrus\Exceptions\Session\SessionDatabaseTableException;
use Zephyrus\Exceptions\Session\SessionDisabledException;
use Zephyrus\Exceptions\Session\SessionFingerprintException;
use Zephyrus\Exceptions\Session\SessionHttpOnlyCookieException;
use Zephyrus\Exceptions\Session\SessionLifetimeException;
use Zephyrus\Exceptions\Session\SessionPathNotExistException;
use Zephyrus\Exceptions\Session\SessionPathNotWritableException;
use Zephyrus\Exceptions\Session\SessionRefreshRateException;
use Zephyrus\Exceptions\Session\SessionRefreshRateProbabilityException;
use Zephyrus\Exceptions\Session\SessionStorageModeException;
use Zephyrus\Exceptions\Session\SessionSupportedRefreshModeException;
use Zephyrus\Exceptions\Session\SessionUseOnlyCookiesException;
use Zephyrus\Network\Request;
use Zephyrus\Utilities\FileSystem\File;

class Session
{
    private ?Request $request = null;
    private SessionConfiguration $configuration;
    private SessionFingerprintManager $fingerprintManager;
    private SessionIdentifierManager $identifierManager;

    /**
     * @param array $configurations
     * @throws SessionDisabledException
     * @throws SessionHttpOnlyCookieException
     * @throws SessionRefreshRateException
     * @throws SessionRefreshRateProbabilityException
     * @throws SessionSupportedRefreshModeException
     * @throws SessionUseOnlyCookiesException
     * @throws SessionDatabaseStructureException
     * @throws SessionDatabaseTableException
     * @throws SessionLifetimeException
     * @throws SessionPathNotExistException
     * @throws SessionPathNotWritableException
     * @throws SessionStorageModeException
     */
    public function __construct(array $configurations = SessionConfiguration::DEFAULT_CONFIGURATIONS)
    {
        // @codeCoverageIgnoreStart
        if (session_status() == PHP_SESSION_DISABLED) {
            throw new SessionDisabledException();
        }
        // @codeCoverageIgnoreEnd

        if (!ini_get('session.use_only_cookies')) {
            throw new SessionUseOnlyCookiesException();
        }

        if (!ini_get('session.cookie_httponly')) {
            throw new SessionHttpOnlyCookieException();
        }

        $this->configuration = new SessionConfiguration($configurations);
        $this->configuration->configure();
        $this->fingerprintManager = $this->configuration->buildFingerprint();
        $this->identifierManager = $this->configuration->buildIdentifier();
    }

    public function setRequest(Request $request): void
    {
        $this->request = $request;
    }

    /**
     * @throws SessionFingerprintException
     */
    public function start(): string
    {
        if (session_status() != PHP_SESSION_ACTIVE) {
            $this->identifierManager->configure();
            $this->fingerprintManager->start($this->request);
        }
        if ($this->fingerprintManager->isInitiated() && !$this->fingerprintManager->isValid($this->request)) {
            throw new SessionFingerprintException();
        }
        if ($this->identifierManager->isObsolete()) {
            $this->regenerate();
        }
        return $this->identifierManager->getId();
    }

    /**
     * Regenerates a session identifier for the current user session and deletes old session file. Useful to prevent
     * some form of session hijacking. Works automatically with the session refresher configurations (refresh_mode and
     * refresh_rate).
     *
     * @return string
     */
    public function regenerate(): string
    {
        session_regenerate_id(true);
        return session_id();
    }

    /**
     * Properly delete the entire session including the cookie expiration and all saved session data.
     */
    public static function destroy(): void
    {
        session_unset();
        setcookie(session_name(), '', 1);
        unset($_COOKIE[session_name()]);
        @session_destroy();
    }

    /**
     * Restart the entire session by regenerating the identifier and deleting all data.
     *
     * @return string
     */
    public function restart(): string
    {
        $this->destroy();
        $this->start();
        return session_id();
    }

    /**
     * Obtains the session value associated with the provided key. If the key doesn't exist in the current session, the
     * default value is thus returned (which is null if not defined by the user).
     *
     * @param string $key
     * @param mixed $defaultValue
     * @return mixed
     */
    public static function get(string $key, mixed $defaultValue = null): mixed
    {
        return $_SESSION[$key] ?? $defaultValue;
    }

    /**
     * Returns all data from the session.
     *
     * @return array
     */
    public static function getAll(): array
    {
        return $_SESSION;
    }

    /**
     * Adds or modifies a session value by providing the desired key and corresponding value. To make sure it does not
     * override an existing value, use the add method.
     *
     * @param string $key
     * @param mixed $value
     */
    public static function set(string $key, mixed $value): void
    {
        $_SESSION[$key] = $value;
    }

    /**
     * Adds the given key / value if the key does not already exist within the session. To update a previously defined
     * key, use the set method.
     *
     * @param string $key
     * @param mixed $value
     * @return bool
     */
    public static function add(string $key, mixed $value): bool
    {
        if (isset($_SESSION[$key])) {
            return false;
        }
        $_SESSION[$key] = $value;
        return true;
    }

    /**
     * Adds of modifies multiple session values at once.
     *
     * @param array $values
     */
    public static function setAll(array $values): void
    {
        foreach ($values as $key => $value) {
            $_SESSION[$key] = $value;
        }
    }

    /**
     * Determines if the specified key exists in the current session. Optionally, if the value argument is used, it also
     * validates that the value is exactly the one provided.
     *
     * @param string $key
     * @param mixed $value
     * @return bool
     */
    public static function has(string $key, mixed $value = null): bool
    {
        return is_null($value)
            ? isset($_SESSION[$key])
            : isset($_SESSION[$key]) && $_SESSION[$key] == $value;
    }

    /**
     * Removes the session data associated with the provided key.
     *
     * @param string $key
     */
    public static function remove(string $key): void
    {
        if (isset($_SESSION[$key])) {
            $_SESSION[$key] = '';
            unset($_SESSION[$key]);
        }
    }

    /**
     * Removes all the session data associated with the given keys.
     *
     * @param array $keys
     */
    public static function removeAll(array $keys): void
    {
        foreach ($keys as $key) {
            self::remove($key);
        }
    }

    /**
     * Empty all the session variables.
     *
     * @return void
     */
    public function clear(): void
    {
        session_unset();
    }

    /**
     * Retrieves the current session identifier.
     *
     * @return string|null
     */
    public function getId(): ?string
    {
        return $this->identifierManager->getId();
    }

    /**
     * Retrieves the current session name.
     *
     * @return string|null
     */
    public function getName(): ?string
    {
        return session_status() == PHP_SESSION_ACTIVE ? session_name() : null;
    }

    public function getIdentifierManager(): SessionIdentifierManager
    {
        return $this->identifierManager;
    }

    public function getFingerprintManager(): SessionFingerprintManager
    {
        return $this->fingerprintManager;
    }

    /**
     * Returns a File instance pointing to the current session file on the server when the session configuration is in
     * mode 'file'.
     *
     * @return File|null
     */
    public function getSessionFile(): ?File
    {
        $path = $this->configuration->getSavePath();
        $sessId = session_id();
        if (!$path || !$sessId) {
            return null;
        }
        return new File($path . '/sess_' . session_id());
    }
}