When building high-concurrency, distributed, or long-running PHP applications, the traditional file-based session storage method can lead to session garbage data accumulation, which negatively impacts performance and stability. To solve this problem, PHP provides the SessionHandlerInterface interface, which allows developers to customize session storage logic. The gc() method is the key to controlling the garbage collection process.
SessionHandlerInterface::gc is a method defined by this interface, which is called when PHP internally determines that session data has expired, in order to perform garbage collection. The method signature is as follows:
public function gc(int $max_lifetime): int|false;
$max_lifetime: The maximum lifetime of the session (in seconds).
Return value: Returns the number of sessions cleaned up or true on success, and false on failure.
After the user customizes the session handler and implements this interface, they can define the strategy for cleaning up expired session data in this method.
PHP's default session storage method is file-based, and its GC mechanism is triggered probabilistically (controlled by session.gc_probability and session.gc_divisor). This means the cleanup logic doesn't run on every request.
For example:
session.gc_probability = 1
session.gc_divisor = 1000
This means garbage collection will be triggered with a probability of 1/1000. This strategy can cause expired sessions to accumulate in low-traffic systems, while in high-traffic systems, it may trigger too frequently and affect performance.
Below is an example of a custom session handler based on database storage, showing how the gc() method can be optimized to improve performance.
class DbSessionHandler implements SessionHandlerInterface {
protected $db;
$this->db = $db;
}
public function open($savePath, $sessionName): bool {
return true;
}
public function close(): bool {
return true;
}
public function read($id): string|false {
$stmt = $this->db->prepare("SELECT data FROM sessions WHERE id = :id AND expires > :now");
$stmt->execute([':id' => $id, ':now' => time()]);
return (string) $stmt->fetchColumn();
}
public function write($id, $data): bool {
$expires = time() + (int)ini_get("session.gc_maxlifetime");
$stmt = $this->db->prepare("
REPLACE INTO sessions (id, data, expires) VALUES (:id, :data, :expires)
");
return $stmt->execute([
':id' => $id,
':data' => $data,
':expires' => $expires,
]);
}
public function destroy($id): bool {
$stmt = $this->db->prepare("DELETE FROM sessions WHERE id = :id");
return $stmt->execute([':id' => $id]);
}
public function gc($max_lifetime): int|false {
$stmt = $this->db->prepare("DELETE FROM sessions WHERE expires < :time");
$stmt->execute([':time' => time()]);
return $stmt->rowCount();
}
}
In the implementation above, the gc() method explicitly deletes all expired session records from the database. Compared to the default probabilistic GC, this approach is more reliable and can be used with system scheduled tasks (such as cron jobs) to perform regular cleanup.
Once the custom handler is defined, it can be enabled as follows:
$handler = new DbSessionHandler($pdo);
session_set_save_handler($handler, true);
session_start();
When used with a database cleanup script or task scheduler, you can set session.gc_probability to 0 to completely disable automatic GC:
session.gc_probability = 0
Then, in your scheduled tasks, periodically execute:
$handler->gc(1440); // For example, run cleanup every 24 minutes
In addition to databases, you can also build session storage and GC mechanisms using Redis, Memcached, or other systems. For example, with Redis, you can leverage its TTL mechanism to automatically expire sessions, without the need to manually implement complex gc() methods:
$redis->setex("sess_$id", $max_lifetime, $data);
For more examples, refer to https://m66.net/examples/session-handler.
By implementing the SessionHandlerInterface::gc() method, PHP developers can gain full control over session garbage collection, which is crucial for building high-performance, scalable web applications. Whether using databases, Redis, or other middleware, leveraging gc() in combination with system-level scheduling tools can significantly improve session management efficiency and system maintainability.
Related Tags:
SessionHandlerInterface