SessionProxy.php
PHP
Path: src/Session/SessionProxy.php
<?php
namespace mini\Session;
use mini\Mini;
/**
* Global proxy for $_SESSION that auto-starts sessions on access
*
* This proxy replaces PHP's native $_SESSION superglobal, providing:
* - Automatic session start on first array access
* - Per-request session instances in fiber/async environments
* - Transparent integration with existing $_SESSION code
*
* Traditional PHP behavior:
* ```php
* session_start(); // Must call manually
* $_SESSION['user'] = 'john'; // Only works after session_start()
* ```
*
* With SessionProxy:
* ```php
* $_SESSION['user'] = 'john'; // Auto-starts session, just works
* $user = $_SESSION['user']; // Returns 'john'
* ```
*
* The proxy delegates all operations to a per-request SessionInterface
* instance obtained from Mini's service container. This enables:
* - Fiber-safe sessions (each request has its own Session instance)
* - Testability (swap SessionInterface for a mock)
* - Runtime agnostic (works in FPM, Swoole, phasync, etc.)
*
* @implements \ArrayAccess<string, mixed>
* @implements \IteratorAggregate<string, mixed>
*/
class SessionProxy implements \ArrayAccess, \Countable, \IteratorAggregate
{
/**
* Get the Session instance for the current request
*/
private function getSession(): SessionInterface
{
return Mini::$mini->get(SessionInterface::class);
}
// =========================================================================
// ArrayAccess implementation
// =========================================================================
/**
* Check if a session key exists
*
* Auto-starts session on first access.
*/
public function offsetExists(mixed $offset): bool
{
return $this->getSession()->has((string) $offset);
}
/**
* Get a session value
*
* Auto-starts session on first access.
*/
public function offsetGet(mixed $offset): mixed
{
return $this->getSession()->get((string) $offset);
}
/**
* Set a session value
*
* Auto-starts session on first access.
*/
public function offsetSet(mixed $offset, mixed $value): void
{
$this->getSession()->set((string) $offset, $value);
}
/**
* Remove a session key
*
* Auto-starts session on first access.
*/
public function offsetUnset(mixed $offset): void
{
$this->getSession()->remove((string) $offset);
}
// =========================================================================
// Countable implementation
// =========================================================================
/**
* Count session items
*
* Auto-starts session on first access.
*/
public function count(): int
{
return count($this->getSession());
}
// =========================================================================
// IteratorAggregate implementation
// =========================================================================
/**
* Get iterator for session data
*
* Auto-starts session on first access.
*
* @return \ArrayIterator<string, mixed>
*/
public function getIterator(): \ArrayIterator
{
return $this->getSession()->getIterator();
}
// =========================================================================
// Debug support
// =========================================================================
/**
* Debug info for var_dump()
*
* @return array<string, mixed>
*/
public function __debugInfo(): array
{
try {
$session = $this->getSession();
return [
'started' => $session->isStarted(),
'id' => $session->isStarted() ? $session->getId() : '(not started)',
'data' => $session->isStarted() ? $session->all() : '(not started)',
];
} catch (\Throwable $e) {
return [
'error' => 'Session service not available: ' . $e->getMessage(),
];
}
}
}