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
10 changes: 2 additions & 8 deletions packages/auth/src/Authentication/SessionAuthenticator.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@

namespace Tempest\Auth\Authentication;

use Tempest\Container\Resettable;
use Tempest\Http\Session\Session;
use Tempest\Http\Session\SessionManager;

final class SessionAuthenticator implements Authenticator, Resettable
final class SessionAuthenticator implements Authenticator
{
public const string AUTHENTICATABLE_KEY = '#authenticatable:id';

Expand Down Expand Up @@ -77,12 +76,7 @@ public function current(): ?Authenticatable
return $this->current;
}

public function reset(): void
{
$this->clearCurrent();
}

private function clearCurrent(): void
public function clearCurrent(): void
{
$this->currentId = null;
$this->currentClass = null;
Expand Down
17 changes: 17 additions & 0 deletions packages/auth/src/Authentication/SessionAuthenticatorReset.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace Tempest\Auth\Authentication;

use Tempest\Container\Resettable;

final readonly class SessionAuthenticatorReset implements Resettable
{
public function __construct(
private SessionAuthenticator $sessionAuthenticator,
) {}

public function reset(): void
{
$this->sessionAuthenticator->clearCurrent();
Comment thread
brendt marked this conversation as resolved.
}
}
5 changes: 2 additions & 3 deletions packages/auth/tests/SessionAuthenticatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
use Tempest\Auth\Authentication\Authenticatable;
use Tempest\Auth\Authentication\AuthenticatableResolver;
use Tempest\Auth\Authentication\SessionAuthenticator;
use Tempest\Container\Resettable;
use Tempest\Auth\Authentication\SessionAuthenticatorReset;
use Tempest\DateTime\DateTime;
use Tempest\Http\Session\Session;
use Tempest\Http\Session\SessionId;
Expand Down Expand Up @@ -100,10 +100,9 @@ public function reset_clears_the_cached_current_authenticatable(): void
authenticatableResolver: $resolver,
);

$this->assertInstanceOf(Resettable::class, $authenticator);
$this->assertSame($authenticatable, $authenticator->current());

$authenticator->reset();
new SessionAuthenticatorReset($authenticator)->reset();

$this->assertSame($authenticatable, $authenticator->current());
$this->assertSame(2, $resolver->resolveCalls);
Expand Down
2 changes: 1 addition & 1 deletion packages/console/src/ConsoleApplication.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public static function boot(
return $container->get(ConsoleApplication::class);
}

public function run(): never
public function run(): void
{
$exitCode = $this->container->get(ExecuteConsoleCommand::class)($this->argumentBag->getCommandName());

Expand Down
18 changes: 18 additions & 0 deletions packages/core/src/DeferredTasksReset.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

namespace Tempest\Core;

use Tempest\Container\Container;
use Tempest\Container\Resettable;

final readonly class DeferredTasksReset implements Resettable
{
public function __construct(
private Container $container,
) {}

public function reset(): void
{
$this->container->unregister(DeferredTasks::class);
}
}
31 changes: 26 additions & 5 deletions packages/core/src/FrameworkKernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,10 @@ final class FrameworkKernel implements Kernel
public function __construct(
public string $root,
/** @var DiscoveryLocation[] */
private array $discoveryLocations = [],
private readonly array $discoveryLocations = [],
?Container $container = null,
?string $internalStorage = null,
private readonly bool $longRunning = false,
) {
$this->container = $container ?? $this->createContainer();

Expand All @@ -51,6 +52,7 @@ public static function boot(
array $discoveryLocations = [],
?Container $container = null,
?string $internalStorage = null,
bool $longRunning = false,
): self {
if (! defined('TEMPEST_START')) {
define('TEMPEST_START', value: hrtime(as_number: true));
Expand All @@ -61,6 +63,7 @@ public static function boot(
discoveryLocations: $discoveryLocations,
container: $container,
internalStorage: $internalStorage,
longRunning: $longRunning,
)
->registerKernel()
->validateRoot()
Expand Down Expand Up @@ -98,12 +101,23 @@ public function validateRoot(): self
return $this;
}

public function shutdown(int|string $status = ''): never
public function shutdown(int|string $status = ''): void
{
$this->finishDeferredTasks()
->event(KernelEvent::SHUTDOWN);
$this->event(KernelEvent::SHUTTING_DOWN)
->finishDeferredTasks();

if ($this->longRunning) {
$this
->event(KernelEvent::RESETTING)
->resetContainer()
->event(KernelEvent::RESET);
}

$this->event(KernelEvent::SHUTDOWN);

exit($status);
if (! $this->longRunning) {
exit($status);
}
}

public function loadComposer(): self
Expand Down Expand Up @@ -236,6 +250,13 @@ public function finishDeferredTasks(): self
return $this;
}

public function resetContainer(): self
{
$this->container->reset();

return $this;
}

public function event(object $event): self
{
if (interface_exists(EventBus::class)) {
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/Kernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@ public static function boot(
?string $internalStorage = null,
): self;

public function shutdown(int|string $status = ''): never;
public function shutdown(int|string $status = ''): void;
}
3 changes: 3 additions & 0 deletions packages/core/src/KernelEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

enum KernelEvent
{
case SHUTTING_DOWN;
case RESETTING;
case RESET;
case BOOTED;
case SHUTDOWN;
}
9 changes: 7 additions & 2 deletions packages/core/src/Tempest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,17 @@
final readonly class Tempest
{
/** @param \Tempest\Discovery\DiscoveryLocation[] $discoveryLocations */
public static function boot(?string $root = null, array $discoveryLocations = [], ?string $internalStorage = null): Container
{
public static function boot(
?string $root = null,
array $discoveryLocations = [],
?string $internalStorage = null,
bool $longRunning = false,
): Container {
$kernel = FrameworkKernel::boot(
root: $root ?? getcwd(),
discoveryLocations: $discoveryLocations,
internalStorage: $internalStorage,
longRunning: $longRunning,
);

return $kernel->container;
Expand Down
6 changes: 6 additions & 0 deletions packages/database/src/Connection/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ interface Connection
{
public function beginTransaction(): bool;

public function inTransaction(): bool;

public function commit(): bool;

public function rollback(): bool;
Expand All @@ -21,4 +23,8 @@ public function prepare(string $sql): PDOStatement;
public function close(): void;

public function connect(): void;

public function reconnect(): void;

public function ping(): bool;
}
30 changes: 27 additions & 3 deletions packages/database/src/Connection/ConnectionInitializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,38 @@

final class ConnectionInitializer implements Initializer
{
/** @var Connection[] */
private static array $connections = [];

#[Singleton]
public function initialize(Container $container): Connection
Comment thread
brendt marked this conversation as resolved.
{
$databaseConfig = $container->get(DatabaseConfig::class);
$config = $container->get(DatabaseConfig::class);
$connectionKey = $this->getConnectionKey($config);

$connection = $config->usePersistentConnection
? self::$connections[$connectionKey] ?? null
: null;

$connection = new PDOConnection($databaseConfig);
$connection->connect();
if (! $connection instanceof Connection) {
$connection = new PDOConnection($config);
$connection->connect();
self::$connections[$connectionKey] = $connection;
} elseif ($connection->ping() === false) {
$connection->reconnect();
}

return $connection;
}

private function getConnectionKey(DatabaseConfig $config): string
{
return hash('xxh128', serialize([
$config->dsn,
$config->username,
$config->options,
$config->password,
$config->tag,
]));
}
}
35 changes: 35 additions & 0 deletions packages/database/src/Connection/ConnectionReset.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

namespace Tempest\Database\Connection;

use Tempest\Container\Container;
use Tempest\Container\GenericContainer;
use Tempest\Container\Resettable;
use Tempest\Database\Exceptions\CouldNotResetConnection;

final readonly class ConnectionReset implements Resettable
{
public function __construct(
private Container $container,
) {}

public function reset(): void
{
// Manually looping over the connection singletons so that we can check whether they still have an active transaction
if ($this->container instanceof GenericContainer) {
$connections = $this->container->getSingletons(Connection::class);

foreach ($connections as $connection) {
if (! $connection instanceof Connection) {
continue;
}

if ($connection->inTransaction()) {
throw new CouldNotResetConnection("There's still an active transaction, make sure to close it before ending the request");
}
}
}

$this->container->unregister(Connection::class, tagged: true);
}
}
9 changes: 9 additions & 0 deletions packages/database/src/Connection/PDOConnection.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,15 @@ public function prepare(string $sql): PDOStatement
return $statement;
}

public function inTransaction(): bool
{
if (! $this->pdo instanceof PDO) {
return false;
}

return $this->pdo->inTransaction();
}

public function ping(): bool
{
try {
Expand Down
42 changes: 31 additions & 11 deletions packages/database/src/DatabaseInitializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@
use Tempest\Reflection\ClassReflector;
use UnitEnum;

final readonly class DatabaseInitializer implements DynamicInitializer
final class DatabaseInitializer implements DynamicInitializer
{
/** @var Connection[] */
private static array $connections = [];

public function canInitialize(ClassReflector $class, null|string|UnitEnum $tag): bool
{
return $class->getType()->matches(Database::class);
Expand All @@ -26,26 +29,43 @@ public function canInitialize(ClassReflector $class, null|string|UnitEnum $tag):
#[Singleton]
public function initialize(ClassReflector $class, null|string|UnitEnum $tag, Container $container): Database
{
$container->singleton(
className: Connection::class,
definition: function () use ($tag, $container) {
$config = $container->get(DatabaseConfig::class, $tag);
$config = $container->get(DatabaseConfig::class, $tag);
$connectionKey = $this->getConnectionKey($config);

$connection = new PDOConnection($config);
$connection->connect();
$connection = $config->usePersistentConnection
? self::$connections[$connectionKey] ?? null
: null;

return $connection;
},
if (! $connection) {
Comment thread
brendt marked this conversation as resolved.
$connection = new PDOConnection($config);
$connection->connect();
self::$connections[$connectionKey] = $connection;
} elseif ($connection->ping() === false) {
$connection->reconnect();
}
Comment thread
brendt marked this conversation as resolved.

$container->singleton(
className: Connection::class,
definition: $connection,
tag: $tag,
);

$connection = $container->get(Connection::class, $tag);

return new GenericDatabase(
connection: $connection,
transactionManager: new GenericTransactionManager($connection),
serializerFactory: $container->get(SerializerFactory::class),
eventBus: $container->get(EventBus::class),
);
}

private function getConnectionKey(DatabaseConfig $config): string
{
return hash('xxh128', serialize([
$config->dsn,
$config->username,
$config->options,
$config->password,
$config->tag,
]));
Comment thread
brendt marked this conversation as resolved.
Comment thread
brendt marked this conversation as resolved.
}
}
18 changes: 18 additions & 0 deletions packages/database/src/DatabaseReset.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

namespace Tempest\Database;

use Tempest\Container\Container;
use Tempest\Container\Resettable;

final readonly class DatabaseReset implements Resettable
{
public function __construct(
private Container $container,
) {}

public function reset(): void
{
$this->container->unregister(Database::class, tagged: true);
}
}
Loading
Loading