Poznajemy w bardzo szczegółowy sposób zagadnienia związane z sesją, jej przechowywaniem i obsługą w języku PHP. Do dzieła.

Sprawdzanie statusu sesji (i warunkowe jej uruchamianie):

 public static function start()
  {
    if (session_status() == PHP_SESSION_NONE) {
      session_start();
    }
  }

Zapisywanie do sesji:

public static function set($key, $value)
  {
    $_SESSION[$key] = $value;
  }

Odczyt z sesji:

public static function get($key, $default = null)
  {
    return isset($_SESSION[$key]) ? $_SESSION[$key] : $default;
  }

Sprawdzanie istnienia danych w sesji:

public static function has($key)
  {
    return isset($_SESSION[$key]);
  }

Oczyszczenie zmiennej w sesji:

public static function clear($key)
  {
    if (isset($_SESSION[$key])) {
      unset($_SESSION[$key]);
    }
  }

Usunięcie zmiennych sesyjnych oraz samej sesji:

public static function clearAll()
  {
    //zmienne:
    session_unset();
    //sesja:
    session_destroy();
    //cookie nadal w twojej przeglądarce btw
  }

Prawidłowe usunięcie zmiennych sesyjnych, sesji oraz cookie sesyjnego po stronie klienta:

public function logout()
  {
    // unset($_SESSION['user']);
    session_destroy();

    // session_regenerate_id();
    $params = session_get_cookie_params();
    setcookie(
      'PHPSESSID',
      '',
      time() - 3600,
      $params['path'],
      $params['domain'],
      $params['secure'],
      $params['httponly']
    );
  }

Usunięcie poprzez ustawienie pustego sessid oraz aktualizację pliku cookie o czas wygaśnięcia będący czasem przeszłym – backend nie może w przeglądarce tak sobie rzeczy usuwać.

Dalej, regeneracja (ponowne stworzenie) session_id dla utrudnienia hakerom roboty przy zachowaniu sesji oraz jej zmiennych i cookie (np. po zalogowaniu):

 public function login(array $formData)
  {
    //(...)

    if (!$user || !$passwordsMatch) {
      throw new ValidationException(['password' => ['Invalid credentials']]);
    }

    session_regenerate_id();

    $_SESSION['user'] = $user['id'];
  }

Wywalenie błędu jeżeli sesja już istnieje (customowy error):

if (session_status() === PHP_SESSION_ACTIVE) {
      throw new SessionException("Session already active.");
    }

Wywalenie błędu jeżeli nagłówki zostały przedwcześnie wysłane (ktoś zapomniał włączyć output buffering):

if (headers_sent($filename, $line)) {
      throw new SessionException("Headers already sent. Consider enabling output buffering. Data outputted from {$filename} - Line: {$line}");
    }

Ustawienie bezpieczeństwa względnie rygorystyczne, ale luzujące SSL ja deweloperce:

session_set_cookie_params([
      'secure' => $_ENV['APP_ENV'] === "production",
      'httponly' => true,
      'samesite' => 'lax'
    ]);

Cały middleware zajmujący się stworzeniem sesji:

class SessionMiddleware implements MiddlewareInterface
{
  public function process(callable $next)
  {
    if (session_status() === PHP_SESSION_ACTIVE) {
      throw new SessionException("Session already active.");
    }

    if (headers_sent($filename, $line)) {
      throw new SessionException("Headers already sent. Consider enabling output buffering. Data outputted from {$filename} - Line: {$line}");
    }

    session_set_cookie_params([
      'secure' => $_ENV['APP_ENV'] === "production",
      'httponly' => true,
      'samesite' => 'lax'
    ]);

    session_start();

    $next();

    session_write_close();
  }
}

Funkcja pokazująca gdzie zapisywane są pliki sesyjne oraz metoda licząca te pliki:

 public static function sessionsCount(){
    $session_path = session_save_path();

    $handle = opendir($session_path);
    $sessions = 0;
        while (($file = readdir($handle)) != FALSE) {
           
            if(in_array($file, ['.', '..']))
                continue;
            if(!preg_match("/^sess/", $file))
                continue;
            $sessions++;
        }
        return $sessions;
  }