Skip to content
Merged
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
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,7 @@ tests/Unit/Log
log/
node_modules
dist
profile/
profile/
db-main.sqlite
db-tenant-1.sqlite
db-tenant-2.sqlite
5 changes: 3 additions & 2 deletions src/Tempest/Auth/src/CurrentUserInitializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,17 @@

use Tempest\Container\Container;
use Tempest\Container\DynamicInitializer;
use Tempest\Container\Tag;
use Tempest\Reflection\ClassReflector;

final readonly class CurrentUserInitializer implements DynamicInitializer
{
public function canInitialize(ClassReflector $class): bool
public function canInitialize(ClassReflector $class, ?string $tag): bool
{
return $class->implements(CanAuthenticate::class);
}

public function initialize(ClassReflector $class, Container $container): object
public function initialize(ClassReflector $class, ?string $tag, Container $container): object
{
$user = $container->get(Authenticator::class)->currentUser();

Expand Down
4 changes: 2 additions & 2 deletions src/Tempest/Container/src/DynamicInitializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

interface DynamicInitializer
{
public function canInitialize(ClassReflector $class): bool;
public function canInitialize(ClassReflector $class, ?string $tag): bool;

public function initialize(ClassReflector $class, Container $container): object;
public function initialize(ClassReflector $class, ?string $tag, Container $container): object;
}
8 changes: 6 additions & 2 deletions src/Tempest/Container/src/GenericContainer.php
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ public function has(string $className, ?string $tag = null): bool

public function singleton(string $className, mixed $definition, ?string $tag = null): self
{
if ($definition instanceof HasTag) {
$tag = $definition->tag;
}

$className = $this->resolveTaggedName($className, $tag);

$this->singletons[$className] = $definition;
Expand Down Expand Up @@ -270,7 +274,7 @@ private function resolve(string $className, ?string $tag = null, mixed ...$param

$object = match (true) {
$initializer instanceof Initializer => $initializer->initialize($this->clone()),
$initializer instanceof DynamicInitializer => $initializer->initialize($class, $this->clone()),
$initializer instanceof DynamicInitializer => $initializer->initialize($class, $tag, $this->clone()),
};

$singleton = $initializerClass->getAttribute(Singleton::class) ?? $initializerClass->getMethod('initialize')->getAttribute(Singleton::class);
Expand Down Expand Up @@ -318,7 +322,7 @@ private function initializerForClass(ClassReflector $target, ?string $tag = null
/** @var DynamicInitializer $initializer */
$initializer = $this->resolve($initializerClass);

if (! $initializer->canInitialize($target)) {
if (! $initializer->canInitialize(class: $target, tag: $tag)) {
continue;
}

Expand Down
12 changes: 12 additions & 0 deletions src/Tempest/Container/src/HasTag.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

namespace Tempest\Container;

use UnitEnum;

interface HasTag
{
public null|string|UnitEnum $tag {
get;
}
}
12 changes: 12 additions & 0 deletions src/Tempest/Container/tests/ContainerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
use Tempest\Container\Tests\Fixtures\ContainerObjectEInitializer;
use Tempest\Container\Tests\Fixtures\DependencyWithBuiltinDependencies;
use Tempest\Container\Tests\Fixtures\DependencyWithTaggedDependency;
use Tempest\Container\Tests\Fixtures\HasTagObject;
use Tempest\Container\Tests\Fixtures\ImplementsInterfaceA;
use Tempest\Container\Tests\Fixtures\InjectA;
use Tempest\Container\Tests\Fixtures\InjectB;
Expand Down Expand Up @@ -557,4 +558,15 @@ public function test_lazy_property_dependency(): void

$this->assertSame('value1', $this->assertSlowerThan(fn () => $instance->dependency->value, $delay));
}

public function test_has_tags_support(): void
{
$container = new GenericContainer();

$container->singleton(HasTagObject::class, new HasTagObject('A', 'tagA'));
$container->singleton(HasTagObject::class, new HasTagObject('B', 'tagB'));

$this->assertSame('A', $container->get(HasTagObject::class, 'tagA')->name);
$this->assertSame('B', $container->get(HasTagObject::class, 'tagB')->name);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,17 @@

use Tempest\Container\Container;
use Tempest\Container\DynamicInitializer;
use Tempest\Container\Tag;
use Tempest\Reflection\ClassReflector;

final class ContainerObjectEInitializer implements DynamicInitializer
{
public function canInitialize(ClassReflector $class): bool
public function canInitialize(ClassReflector $class, ?string $tag): bool
{
return $class->getName() === ContainerObjectE::class;
}

public function initialize(ClassReflector $class, Container $container): object
public function initialize(ClassReflector $class, ?string $tag, Container $container): object
{
return new ContainerObjectE();
}
Expand Down
14 changes: 14 additions & 0 deletions src/Tempest/Container/tests/Fixtures/HasTagObject.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace Tempest\Container\Tests\Fixtures;

use Tempest\Container\HasTag;
use UnitEnum;

final class HasTagObject implements HasTag
{
public function __construct(
public string $name,
public null|string|UnitEnum $tag = null,
) {}
}
3 changes: 2 additions & 1 deletion src/Tempest/Database/src/Config/DatabaseConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@

namespace Tempest\Database\Config;

use Tempest\Container\HasTag;
use Tempest\Database\Tables\NamingStrategy;

interface DatabaseConfig
interface DatabaseConfig extends HasTag
{
public string $dsn {
get;
Expand Down
2 changes: 2 additions & 0 deletions src/Tempest/Database/src/Config/MysqlConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use SensitiveParameter;
use Tempest\Database\Tables\NamingStrategy;
use Tempest\Database\Tables\PluralizedSnakeCaseStrategy;
use UnitEnum;

final class MysqlConfig implements DatabaseConfig
{
Expand Down Expand Up @@ -35,5 +36,6 @@ public function __construct(
#[SensitiveParameter]
public string $database = 'app',
public NamingStrategy $namingStrategy = new PluralizedSnakeCaseStrategy(),
public null|string|UnitEnum $tag = null,
) {}
}
2 changes: 2 additions & 0 deletions src/Tempest/Database/src/Config/PostgresConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use SensitiveParameter;
use Tempest\Database\Tables\NamingStrategy;
use Tempest\Database\Tables\PluralizedSnakeCaseStrategy;
use UnitEnum;

final class PostgresConfig implements DatabaseConfig
{
Expand Down Expand Up @@ -37,5 +38,6 @@ public function __construct(
#[SensitiveParameter]
public string $database = 'app',
public NamingStrategy $namingStrategy = new PluralizedSnakeCaseStrategy(),
public null|string|UnitEnum $tag = null,
) {}
}
2 changes: 2 additions & 0 deletions src/Tempest/Database/src/Config/SQLiteConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use SensitiveParameter;
use Tempest\Database\Tables\NamingStrategy;
use Tempest\Database\Tables\PluralizedSnakeCaseStrategy;
use UnitEnum;

final class SQLiteConfig implements DatabaseConfig
{
Expand All @@ -33,5 +34,6 @@ public function __construct(
#[SensitiveParameter]
public string $path = 'localhost',
public NamingStrategy $namingStrategy = new PluralizedSnakeCaseStrategy(),
public null|string|UnitEnum $tag = null,
) {}
}
2 changes: 1 addition & 1 deletion src/Tempest/Database/src/Connection/PDOConnection.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ final class PDOConnection implements Connection
private ?PDO $pdo = null;

public function __construct(
private readonly DatabaseConfig $config,
private(set) readonly DatabaseConfig $config,
) {}

public function beginTransaction(): bool
Expand Down
31 changes: 25 additions & 6 deletions src/Tempest/Database/src/DatabaseInitializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,38 @@
namespace Tempest\Database;

use Tempest\Container\Container;
use Tempest\Container\Initializer;
use Tempest\Container\DynamicInitializer;
use Tempest\Container\Singleton;
use Tempest\Database\Config\DatabaseConfig;
use Tempest\Database\Connection\Connection;
use Tempest\Database\Transactions\TransactionManager;
use Tempest\Database\Connection\PDOConnection;
use Tempest\Database\Transactions\GenericTransactionManager;
use Tempest\Reflection\ClassReflector;

final readonly class DatabaseInitializer implements Initializer
final readonly class DatabaseInitializer implements DynamicInitializer
{
public function canInitialize(ClassReflector $class, ?string $tag): bool
{
return $class->getType()->matches(Database::class);
}

#[Singleton]
public function initialize(Container $container): Database
public function initialize(ClassReflector $class, ?string $tag, Container $container): Database
{
$container->singleton(Connection::class, function () use ($tag, $container) {
$config = $container->get(DatabaseConfig::class, $tag);

$connection = new PDOConnection($config);
$connection->connect();

return $connection;
});

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

return new GenericDatabase(
$container->get(Connection::class),
$container->get(TransactionManager::class),
$connection,
new GenericTransactionManager($connection),
);
}
}
4 changes: 2 additions & 2 deletions src/Tempest/Database/src/GenericDatabase.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
final readonly class GenericDatabase implements Database
{
public function __construct(
private Connection $connection,
private TransactionManager $transactionManager,
private(set) Connection $connection,
private(set) TransactionManager $transactionManager,
) {}

public function execute(Query $query): void
Expand Down
5 changes: 3 additions & 2 deletions src/Tempest/Router/src/RouteBindingInitializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,18 @@

use Tempest\Container\Container;
use Tempest\Container\DynamicInitializer;
use Tempest\Container\Tag;
use Tempest\Reflection\ClassReflector;
use Tempest\Router\Exceptions\NotFoundException;

final class RouteBindingInitializer implements DynamicInitializer
{
public function canInitialize(ClassReflector $class): bool
public function canInitialize(ClassReflector $class, ?string $tag): bool
{
return $class->getType()->matches(Bindable::class);
}

public function initialize(ClassReflector $class, Container $container): object
public function initialize(ClassReflector $class, ?string $tag, Container $container): object
{
$matchedRoute = $container->get(MatchedRoute::class);

Expand Down
5 changes: 3 additions & 2 deletions src/Tempest/Router/src/RouteEnumBindingInitializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,18 @@
use BackedEnum;
use Tempest\Container\Container;
use Tempest\Container\DynamicInitializer;
use Tempest\Container\Tag;
use Tempest\Reflection\ClassReflector;
use Tempest\Router\Exceptions\NotFoundException;

final class RouteEnumBindingInitializer implements DynamicInitializer
{
public function canInitialize(ClassReflector $class): bool
public function canInitialize(ClassReflector $class, ?string $tag): bool
{
return $class->getType()->matches(BackedEnum::class);
}

public function initialize(ClassReflector $class, Container $container): object
public function initialize(ClassReflector $class, ?string $tag, Container $container): object
{
$matchedRoute = $container->get(MatchedRoute::class);

Expand Down
5 changes: 3 additions & 2 deletions src/Tempest/View/src/Renderers/BladeInitializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@
use Tempest\Container\Container;
use Tempest\Container\DynamicInitializer;
use Tempest\Container\Singleton;
use Tempest\Container\Tag;
use Tempest\Reflection\ClassReflector;

use function Tempest\internal_storage_path;

final readonly class BladeInitializer implements DynamicInitializer
{
public function canInitialize(ClassReflector $class): bool
public function canInitialize(ClassReflector $class, ?string $tag): bool
{
if (! class_exists(Blade::class)) {
return false;
Expand All @@ -24,7 +25,7 @@ public function canInitialize(ClassReflector $class): bool
}

#[Singleton]
public function initialize(ClassReflector $class, Container $container): object
public function initialize(ClassReflector $class, ?string $tag, Container $container): object
{
$bladeConfig = $container->get(BladeConfig::class);

Expand Down
4 changes: 2 additions & 2 deletions src/Tempest/View/src/Renderers/TwigInitializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

final readonly class TwigInitializer implements DynamicInitializer
{
public function canInitialize(ClassReflector $class): bool
public function canInitialize(ClassReflector $class, ?string $tag): bool
{
if (! class_exists(Environment::class)) {
return false;
Expand All @@ -23,7 +23,7 @@ public function canInitialize(ClassReflector $class): bool
}

#[Singleton]
public function initialize(ClassReflector $class, Container $container): object
public function initialize(ClassReflector $class, ?string $tag, Container $container): object
{
$twigConfig = $container->get(TwigConfig::class);
$twigLoader = new FilesystemLoader($twigConfig->viewPaths);
Expand Down
43 changes: 43 additions & 0 deletions tests/Integration/Container/TaggedDynamicInitializerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

namespace Tests\Tempest\Integration\Container;

use PHPUnit\Framework\TestCase;
use Tempest\Container\GenericContainer;
use Tempest\Database\Config\SQLiteConfig;
use Tempest\Database\Database;
use Tempest\Database\DatabaseInitializer;

final class TaggedDynamicInitializerTest extends TestCase
{
public function test_resolve(): void
{
$container = new GenericContainer();
$container->addInitializer(DatabaseInitializer::class);

$container->config(new SQLiteConfig(
path: 'db-main.sqlite',
));

$container->config(new SQLiteConfig(
path: 'db-tenant-1.sqlite',
tag: 'tenant-1',
));

$container->config(new SQLiteConfig(
path: 'db-tenant-2.sqlite',
tag: 'tenant-2',
));

$tenant1 = $container->get(Database::class, tag: 'tenant-1');
$tenant2 = $container->get(Database::class, tag: 'tenant-2');
$main = $container->get(Database::class);

/** @phpstan-ignore-next-line */
$this->assertSame('db-tenant-1.sqlite', $tenant1->connection->config->path);
/** @phpstan-ignore-next-line */
$this->assertSame('db-tenant-2.sqlite', $tenant2->connection->config->path);
/** @phpstan-ignore-next-line */
$this->assertSame('db-main.sqlite', $main->connection->config->path);
}
}
Loading
Loading