Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 61 additions & 5 deletions docs/1-essentials/01-routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -919,7 +919,9 @@ public function store(Todo $todo): Redirect

### Session configuration

Tempest supports file and database-based sessions, the former being the default option. Sessions can be configured by creating a `session.config.php` file, in which the expiration time and the session driver can be specified.
Tempest supports file, Redis and database-based sessions, the former being the default option.

Sessions can be configured by creating a `session.config.php` file [anywhere](../1-essentials/06-configuration.md#configuration-files), in which the expiration time and the [clean up strategy](#session-cleaning) can be configured.

#### File sessions

Expand All @@ -939,13 +941,13 @@ return new FileSessionConfig(

Tempest provides a database-based session driver, particularly useful for applications that run on multiple servers, as session data can be shared across all instances.

Before using database sessions, a dedicated table is needed. Tempest provides a migration that can be installed using its installer:
Before using database sessions, a dedicated table is needed. Tempest provides a dedicated sessions installer that can publish file, database, or Redis session configuration:

```sh
./tempest install sessions:database
./tempest install sessions
```

This installer also suggests creating the configuration file that sets up database sessions, with a default expiration of 30 days:
When choosing the database strategy, the installer can also publish a migration and the configuration file that sets up database sessions, with a default expiration of 30 days:

```php app/Sessions/session.config.php
use Tempest\Http\Session\Config\DatabaseSessionConfig;
Expand All @@ -960,7 +962,61 @@ return new DatabaseSessionConfig(

Sessions expire based on the last activity time. This means that as long as a user is actively using the application, their session remains valid.

Outdated sessions must occasionally be cleaned up. Tempest provides a built-in command to do so, `session:clean`. This command uses the [scheduler](../2-features/11-scheduling.md): with scheduling enabled, it automatically runs behind the scenes.
By default, Tempest removes expired session randomly at the end of a request, in a deferred task. This can be configured by specifying a {b`Tempest\Http\Session\CleanupStrategy`} in the [session configuration](#session-configuration).

```php app/Sessions/session.config.php
use Tempest\Http\Session\Config\DatabaseSessionConfig;
use Tempest\DateTime\Duration;

return new DatabaseSessionConfig(
expiration: Duration::days(30),
cleanupStrategy: CleanupStrategy::EVERY_REQUEST,
);
```

The default behavior is great for most applications. However, at a certain scale, the performance of random cleanup can decrease as the number of sessions grows. In that case, it is recommended to disable request-based session cleaning and switch to a scheduled cleanup strategy:

:::code-group

```php app/Sessions/session.config.php
use Tempest\Http\Session\Config\DatabaseSessionConfig;
use Tempest\DateTime\Duration;

return new DatabaseSessionConfig(
expiration: Duration::days(30),
cleanupStrategy: CleanupStrategy::DISABLED,
);
```

```php app/Sessions/CleanupSessionsCommand.php
namespace App\Sessions;

use Tempest\Console\ConsoleCommand;
use Tempest\Console\Schedule;
use Tempest\Console\Scheduler\Every;
use Tempest\Http\Session\SessionManager;

final readonly class CleanupSessionsCommand
{
public function __construct(
private SessionManager $sessionManager,
) {
}

#[ConsoleCommand(name: 'session:clean')]
#[Schedule(Every::MINUTE)]
public function __invoke(): void
{
$this->sessionManager->deleteExpiredSessions();
}
}
```

:::

:::info
This cleanup command can be installed in your codebase by running the `./tempest install sessions` command.
:::

## Deferring tasks

Expand Down
6 changes: 3 additions & 3 deletions docs/1-essentials/06-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,18 @@ The configuration object above instructs Tempest to use PostgreSQL as its databa
To access a configuration object, you may inject it from the container like any other dependency.

```php
use Tempest\Core\Environment;
use Tempest\Core\AppConfig;

final readonly class AboutController
{
public function __construct(
private Environment $environment,
private AppConfig $config,
) {}

#[Get('/')]
public function __invoke(): View
{
return view('about.view.php', environment: $this->environment);
return view('about.view.php', name: $this->config->name);
}
}
```
Expand Down
17 changes: 11 additions & 6 deletions packages/core/src/PublishesFiles.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,20 @@ trait PublishesFiles
* @param string $source The path to the source file.
* @param string $destination The path to the destination file.
* @param Closure(string $source, string $destination): void|null $callback A callback to run after the file is published.
* @param bool $confirm Whether to ask for create confirmation before publishing.
*/
public function publish(string $source, string $destination, ?Closure $callback = null): string|false
public function publish(string $source, string $destination, ?Closure $callback = null, bool $confirm = true): string|false
{
try {
if (! $this->console->confirm(
question: sprintf('Do you want to create <file="%s" />?', $this->friendlyFileName($destination)),
default: true,
)) {
throw new FileGenerationWasAborted('Skipped.');
if ($confirm) {
$shouldContinue = $this->console->confirm(
question: sprintf('Do you want to create <file="%s" />?', $this->friendlyFileName($destination)),
default: true,
);

if (! $shouldContinue) {
throw new FileGenerationWasAborted('Skipped.');
}
}

if (! $this->askForOverride($destination)) {
Expand Down
2 changes: 2 additions & 0 deletions packages/http/src/Session/CleanupSessionsCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
use Tempest\Console\ConsoleCommand;
use Tempest\Console\Schedule;
use Tempest\Console\Scheduler\Every;
use Tempest\Discovery\SkipDiscovery;
use Tempest\EventBus\EventBus;

#[SkipDiscovery]
final readonly class CleanupSessionsCommand
{
public function __construct(
Expand Down
21 changes: 21 additions & 0 deletions packages/http/src/Session/CleanupStrategy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace Tempest\Http\Session;

enum CleanupStrategy
{
/**
* Expired sessions will be cleaned up after every request. This may cause performance issues on high-traffic applications, but ensures that expired sessions are removed as soon as possible.
*/
case EVERY_REQUEST;

/**
* Expired sessions will be cleaned up after a random request. This is the default strategy, and provides a good balance between performance and cleanup frequency.
*/
case RANDOM_REQUESTS;

/**
* Expired sessions will only be cleaned up when the `session:clean` command is executed. This is the most performant strategy, but requires that the command is scheduled to run regularly (e.g. every minute).
*/
case DISABLED;
}
3 changes: 3 additions & 0 deletions packages/http/src/Session/Config/DatabaseSessionConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,19 @@

use Tempest\Container\Container;
use Tempest\DateTime\Duration;
use Tempest\Http\Session\CleanupStrategy;
use Tempest\Http\Session\Managers\DatabaseSessionManager;
use Tempest\Http\Session\SessionConfig;

final class DatabaseSessionConfig implements SessionConfig
{
/**
* @param Duration $expiration Time required for a session to expire.
* @param CleanupStrategy $cleanupStrategy Strategy for cleaning up expired sessions. Defaults to `RANDOM_REQUESTS`, which provides a good balance between performance and cleanup frequency.
*/
public function __construct(
private(set) Duration $expiration,
private(set) CleanupStrategy $cleanupStrategy = CleanupStrategy::RANDOM_REQUESTS,
) {}

public function createManager(Container $container): DatabaseSessionManager
Expand Down
3 changes: 3 additions & 0 deletions packages/http/src/Session/Config/FileSessionConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Tempest\Container\Container;
use Tempest\DateTime\Duration;
use Tempest\Http\Session\CleanupStrategy;
use Tempest\Http\Session\Managers\FileSessionManager;
use Tempest\Http\Session\SessionConfig;

Expand All @@ -12,9 +13,11 @@ final class FileSessionConfig implements SessionConfig
/**
* @param string $path Path to the sessions storage directory, relative to the internal storage.
* @param Duration $expiration Time required for a session to expire.
* @param CleanupStrategy $cleanupStrategy Strategy for cleaning up expired sessions. Defaults to `RANDOM_REQUESTS`, which provides a good balance between performance and cleanup frequency.
*/
public function __construct(
public Duration $expiration,
private(set) CleanupStrategy $cleanupStrategy = CleanupStrategy::RANDOM_REQUESTS,
public string $path = 'sessions',
) {}

Expand Down
3 changes: 3 additions & 0 deletions packages/http/src/Session/Config/RedisSessionConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,19 @@

use Tempest\Container\Container;
use Tempest\DateTime\Duration;
use Tempest\Http\Session\CleanupStrategy;
use Tempest\Http\Session\Managers\RedisSessionManager;
use Tempest\Http\Session\SessionConfig;

final class RedisSessionConfig implements SessionConfig
{
/**
* @param Duration $expiration Time required for a session to expire.
* @param CleanupStrategy $cleanupStrategy Strategy for cleaning up expired sessions. Defaults to `DISABLED`, because sessions expire automatically in Redis.
*/
public function __construct(
private(set) Duration $expiration,
private(set) CleanupStrategy $cleanupStrategy = CleanupStrategy::DISABLED,
readonly string $prefix = 'session:',
) {}

Expand Down

This file was deleted.

Loading
Loading