Skip to content

fix(session): guard session_destroy() when no active PHP session#162

Merged
ralflang merged 1 commit into
FRAMEWORK_6_0from
fix/session_destroy_guard
Jun 17, 2026
Merged

fix(session): guard session_destroy() when no active PHP session#162
ralflang merged 1 commit into
FRAMEWORK_6_0from
fix/session_destroy_guard

Conversation

@TDannhauer

Copy link
Copy Markdown
Contributor

Session destroy guard fix

Conventional commit message

fix(session): guard session_destroy() when no active PHP session

Horde_Session initializes $_SESSION to [] in its constructor before
session_start() runs. SessionLifecycle::destroy() and the legacy
Horde_Session::destroy() fallback used isset($_SESSION) as the guard,
which is always true once the shim is constructed. That caused repeated
PHP warnings:

  session_destroy(): Trying to destroy uninitialized session

Replace the isset($_SESSION) check with session_status() ===
PHP_SESSION_ACTIVE. SessionLifecycle also consults $this->active so the
normal lifecycle path remains covered when the local flag is authoritative.

Pull request

Title

fix(session): guard session_destroy() when no active PHP session

Body

Summary

  • Fix repeated PHP warnings when SessionLifecycle::destroy() or Horde_Session::destroy() runs without an active PHP session (e.g. logout/auth cleanup before session_start(), or processFlags() handling a pending destroy marker).
  • Replace isset($_SESSION) with session_status() === PHP_SESSION_ACTIVE as the guard before calling session_destroy().
  • In SessionLifecycle::destroy(), also check $this->active so the lifecycle’s tracked state remains the primary gate on the normal code path.

Problem

Horde_Session::__construct() ensures $_SESSION exists as an empty array even when PHP’s session module has not been started:

if (!isset($_SESSION)) {
    $_SESSION = [];
}

Both destroy paths previously guarded session_destroy() with isset($_SESSION), which is true in that situation. PHP 8+ then emits:

session_destroy(): Trying to destroy uninitialized session

This showed up repeatedly in Horde logs during logout and other auth-cleanup flows.

Changes

File Change
src/Session/SessionLifecycle.php Guard session_destroy() with $this->active || session_status() === PHP_SESSION_ACTIVE
lib/Horde/Session.php Guard legacy fallback session_destroy() with session_status() === PHP_SESSION_ACTIVE

Non-destructive cleanup ($_SESSION = [], rebuild HordeSession, clear secrets/lifecycle flags) is unchanged and still runs when no PHP session is active.

Test plan

  • Log in, log out — no session_destroy(): Trying to destroy uninitialized session warnings in the Horde log.
  • Hit an unauthenticated endpoint that triggers auth cleanup with $destroy = true — no warnings.
  • Normal authenticated request lifecycle unchanged (session data persists across requests).
  • SessionLifecycleTest unit suite passes (vendor/bin/phpunit test/Unit/Session/SessionLifecycleTest.php).

Horde_Session initializes $_SESSION to [] in its constructor before
session_start() runs. SessionLifecycle::destroy() and the legacy
Horde_Session::destroy() fallback used isset($_SESSION) as the guard,
which is always true once the shim is constructed. That caused repeated
PHP warnings:
  session_destroy(): Trying to destroy uninitialized session
Replace the isset($_SESSION) check with session_status() ===
PHP_SESSION_ACTIVE. SessionLifecycle also consults $this->active so the
normal lifecycle path remains covered when the local flag is authoritative.
@TDannhauer TDannhauer requested a review from ralflang June 16, 2026 20:22
@ralflang

Copy link
Copy Markdown
Member

Great, thank you!

@ralflang ralflang merged commit 0ec355b into FRAMEWORK_6_0 Jun 17, 2026
0 of 4 checks passed
@TDannhauer TDannhauer deleted the fix/session_destroy_guard branch June 17, 2026 13:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants