Skip to content

Commit 2049f6e

Browse files
authored
feat(core): partial discovery cache (#763)
1 parent c7f964f commit 2049f6e

40 files changed

+743
-453
lines changed

src/Tempest/Cache/src/CacheConfig.php

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Tempest\Cache;
66

77
use Psr\Cache\CacheItemPoolInterface;
8+
use Tempest\Core\Commands\DiscoveryGenerateCommand;
89
use function Tempest\env;
910

1011
final class CacheConfig
@@ -18,7 +19,7 @@ final class CacheConfig
1819

1920
public bool $viewCache = false;
2021

21-
public bool $discoveryCache = false;
22+
public DiscoveryCacheStrategy $discoveryCache;
2223

2324
public function __construct(
2425
public string $directory = __DIR__ . '/../../../../.cache',
@@ -28,14 +29,39 @@ public function __construct(
2829
?bool $enable = null,
2930
) {
3031
$this->enable = $enable ?? env('CACHE') ?? null;
31-
$this->projectCache = (bool) env('PROJECT_CACHE', false);
32-
$this->viewCache = (bool) env('VIEW_CACHE', false);
33-
$this->discoveryCache = (bool) env('DISCOVERY_CACHE', false);
32+
$this->projectCache = (bool)env('PROJECT_CACHE', false);
33+
$this->viewCache = (bool)env('VIEW_CACHE', false);
34+
$this->discoveryCache = $this->resolveDiscoveryCacheStrategy();
3435
}
3536

3637
/** @param class-string<\Tempest\Cache\Cache> $className */
3738
public function addCache(string $className): void
3839
{
3940
$this->caches[] = $className;
4041
}
42+
43+
private function resolveDiscoveryCacheStrategy(): DiscoveryCacheStrategy
44+
{
45+
if (PHP_SAPI === 'cli') {
46+
$command = $_SERVER['argv'][1] ?? null;
47+
48+
if ($command === 'dg' || $command === 'discovery:generate') {
49+
return DiscoveryCacheStrategy::NONE;
50+
}
51+
}
52+
53+
$current = DiscoveryCacheStrategy::make(env('DISCOVERY_CACHE'));
54+
55+
if ($current === DiscoveryCacheStrategy::NONE) {
56+
return $current;
57+
}
58+
59+
$original = DiscoveryCacheStrategy::make(@file_get_contents(DiscoveryGenerateCommand::CURRENT_DISCOVERY_STRATEGY));
60+
61+
if ($current !== $original) {
62+
return DiscoveryCacheStrategy::INVALID;
63+
}
64+
65+
return $current;
66+
}
4167
}

src/Tempest/Cache/src/CacheDiscovery.php

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,34 +4,31 @@
44

55
namespace Tempest\Cache;
66

7-
use Tempest\Container\Container;
87
use Tempest\Core\Discovery;
9-
use Tempest\Core\HandlesDiscoveryCache;
8+
use Tempest\Core\DiscoveryLocation;
9+
use Tempest\Core\IsDiscovery;
1010
use Tempest\Reflection\ClassReflector;
1111

12-
final readonly class CacheDiscovery implements Discovery
12+
final class CacheDiscovery implements Discovery
1313
{
14-
use HandlesDiscoveryCache;
14+
use IsDiscovery;
1515

1616
public function __construct(
17-
private CacheConfig $cacheConfig,
17+
private readonly CacheConfig $cacheConfig,
1818
) {
1919
}
2020

21-
public function discover(ClassReflector $class): void
21+
public function discover(DiscoveryLocation $location, ClassReflector $class): void
2222
{
2323
if ($class->implements(Cache::class)) {
24-
$this->cacheConfig->addCache($class->getName());
24+
$this->discoveryItems->add($location, $class->getName());
2525
}
2626
}
2727

28-
public function createCachePayload(): string
28+
public function apply(): void
2929
{
30-
return serialize($this->cacheConfig->caches);
31-
}
32-
33-
public function restoreCachePayload(Container $container, string $payload): void
34-
{
35-
$this->cacheConfig->caches = unserialize($payload);
30+
foreach ($this->discoveryItems as $className) {
31+
$this->cacheConfig->addCache($className);
32+
}
3633
}
3734
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tempest\Cache;
6+
7+
enum DiscoveryCacheStrategy: string
8+
{
9+
case FULL = 'all';
10+
case PARTIAL = 'partial';
11+
case NONE = 'none';
12+
case INVALID = 'invalid';
13+
14+
public static function make(mixed $input): self
15+
{
16+
return match ($input) {
17+
true, 'true', '1', 1, 'all', 'full' => self::FULL,
18+
'partial' => self::PARTIAL,
19+
'invalid' => self::INVALID,
20+
default => self::NONE,
21+
};
22+
}
23+
24+
public function isEnabled(): bool
25+
{
26+
return match ($this) {
27+
self::FULL, self::PARTIAL => true,
28+
default => false,
29+
};
30+
}
31+
32+
public function isValid(): bool
33+
{
34+
return $this !== self::INVALID;
35+
}
36+
}

src/Tempest/CommandBus/src/CommandBusDiscovery.php

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,21 @@
44

55
namespace Tempest\CommandBus;
66

7-
use Tempest\Container\Container;
87
use Tempest\Core\Discovery;
9-
use Tempest\Core\HandlesDiscoveryCache;
8+
use Tempest\Core\DiscoveryLocation;
9+
use Tempest\Core\IsDiscovery;
1010
use Tempest\Reflection\ClassReflector;
11-
use Tempest\Reflection\MethodReflector;
1211

13-
final readonly class CommandBusDiscovery implements Discovery
12+
final class CommandBusDiscovery implements Discovery
1413
{
15-
use HandlesDiscoveryCache;
14+
use IsDiscovery;
1615

1716
public function __construct(
18-
private CommandBusConfig $commandBusConfig,
17+
private readonly CommandBusConfig $commandBusConfig,
1918
) {
2019
}
2120

22-
public function discover(ClassReflector $class): void
21+
public function discover(DiscoveryLocation $location, ClassReflector $class): void
2322
{
2423
foreach ($class->getPublicMethods() as $method) {
2524
$commandHandler = $method->getAttribute(CommandHandler::class);
@@ -40,23 +39,18 @@ public function discover(ClassReflector $class): void
4039
continue;
4140
}
4241

42+
$this->discoveryItems->add($location, [$commandHandler, $type->getName(), $method]);
43+
}
44+
}
45+
46+
public function apply(): void
47+
{
48+
foreach ($this->discoveryItems as [$commandHandler, $commandName, $method]) {
4349
$this->commandBusConfig->addHandler(
4450
commandHandler: $commandHandler,
45-
commandName: $type->getName(),
51+
commandName: $commandName,
4652
handler: $method,
4753
);
4854
}
4955
}
50-
51-
public function createCachePayload(): string
52-
{
53-
return serialize($this->commandBusConfig->handlers);
54-
}
55-
56-
public function restoreCachePayload(Container $container, string $payload): void
57-
{
58-
$handlers = unserialize($payload, ['allowed_classes' => [CommandHandler::class, MethodReflector::class]]);
59-
60-
$this->commandBusConfig->handlers = $handlers;
61-
}
6256
}

src/Tempest/Console/src/Discovery/ConsoleCommandDiscovery.php

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,21 @@
66

77
use Tempest\Console\ConsoleCommand;
88
use Tempest\Console\ConsoleConfig;
9-
use Tempest\Container\Container;
109
use Tempest\Core\Discovery;
11-
use Tempest\Core\HandlesDiscoveryCache;
10+
use Tempest\Core\DiscoveryLocation;
11+
use Tempest\Core\IsDiscovery;
1212
use Tempest\Reflection\ClassReflector;
13-
use Tempest\Reflection\MethodReflector;
1413

15-
final readonly class ConsoleCommandDiscovery implements Discovery
14+
final class ConsoleCommandDiscovery implements Discovery
1615
{
17-
use HandlesDiscoveryCache;
16+
use IsDiscovery;
1817

1918
public function __construct(
20-
private ConsoleConfig $consoleConfig,
19+
private readonly ConsoleConfig $consoleConfig,
2120
) {
2221
}
2322

24-
public function discover(ClassReflector $class): void
23+
public function discover(DiscoveryLocation $location, ClassReflector $class): void
2524
{
2625
foreach ($class->getPublicMethods() as $method) {
2726
$consoleCommand = $method->getAttribute(ConsoleCommand::class);
@@ -30,19 +29,14 @@ public function discover(ClassReflector $class): void
3029
continue;
3130
}
3231

33-
$this->consoleConfig->addCommand($method, $consoleCommand);
32+
$this->discoveryItems->add($location, [$method, $consoleCommand]);
3433
}
3534
}
3635

37-
public function createCachePayload(): string
38-
{
39-
return serialize($this->consoleConfig->commands);
40-
}
41-
42-
public function restoreCachePayload(Container $container, string $payload): void
36+
public function apply(): void
4337
{
44-
$commands = unserialize($payload, ['allowed_classes' => [ConsoleCommand::class, MethodReflector::class]]);
45-
46-
$this->consoleConfig->commands = $commands;
38+
foreach ($this->discoveryItems as [$method, $consoleCommand]) {
39+
$this->consoleConfig->addCommand($method, $consoleCommand);
40+
}
4741
}
4842
}

src/Tempest/Console/src/Discovery/ScheduleDiscovery.php

Lines changed: 11 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,22 @@
66

77
use Tempest\Console\ConsoleCommand;
88
use Tempest\Console\Schedule;
9-
use Tempest\Console\Scheduler\Interval;
10-
use Tempest\Console\Scheduler\ScheduledInvocation;
119
use Tempest\Console\Scheduler\SchedulerConfig;
12-
use Tempest\Container\Container;
1310
use Tempest\Core\Discovery;
14-
use Tempest\Core\HandlesDiscoveryCache;
11+
use Tempest\Core\DiscoveryLocation;
12+
use Tempest\Core\IsDiscovery;
1513
use Tempest\Reflection\ClassReflector;
16-
use Tempest\Reflection\MethodReflector;
1714

1815
final class ScheduleDiscovery implements Discovery
1916
{
20-
use HandlesDiscoveryCache;
17+
use IsDiscovery;
2118

2219
public function __construct(
2320
private readonly SchedulerConfig $schedulerConfig,
2421
) {
2522
}
2623

27-
public function discover(ClassReflector $class): void
24+
public function discover(DiscoveryLocation $location, ClassReflector $class): void
2825
{
2926
foreach ($class->getPublicMethods() as $method) {
3027
$schedule = $method->getAttribute(Schedule::class);
@@ -35,31 +32,18 @@ public function discover(ClassReflector $class): void
3532

3633
$command = $method->getAttribute(ConsoleCommand::class);
3734

35+
$this->discoveryItems->add($location, [$method, $command, $schedule]);
36+
}
37+
}
38+
39+
public function apply(): void
40+
{
41+
foreach ($this->discoveryItems as [$method, $command, $schedule]) {
3842
if ($command) {
3943
$this->schedulerConfig->addCommandInvocation($method, $command, $schedule);
4044
} else {
4145
$this->schedulerConfig->addMethodInvocation($method, $schedule);
4246
}
4347
}
4448
}
45-
46-
public function createCachePayload(): string
47-
{
48-
return serialize($this->schedulerConfig->scheduledInvocations);
49-
}
50-
51-
public function restoreCachePayload(Container $container, string $payload): void
52-
{
53-
$scheduledInvocations = unserialize($payload, [
54-
'allowed_classes' => [
55-
ScheduledInvocation::class,
56-
Schedule::class,
57-
Interval::class,
58-
ConsoleCommand::class,
59-
MethodReflector::class,
60-
],
61-
]);
62-
63-
$this->schedulerConfig->scheduledInvocations = $scheduledInvocations;
64-
}
6549
}

src/Tempest/Console/src/Middleware/OverviewMiddleware.php

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,7 @@ public function __invoke(Invocation $invocation, ConsoleMiddlewareCallable $next
3636
private function renderOverview(bool $showHidden = false): void
3737
{
3838
$this->console
39-
->writeln("<h1>{$this->consoleConfig->name}</h1>")
40-
->when(
41-
expression: $this->discoveryCache->isEnabled(),
42-
callback: fn (Console $console) => $console->error('Discovery cache is enabled!')
43-
);
39+
->writeln("<h1>{$this->consoleConfig->name}</h1>");
4440

4541
/** @var \Tempest\Console\ConsoleCommand[][] $commands */
4642
$commands = [];
@@ -68,5 +64,11 @@ private function renderOverview(bool $showHidden = false): void
6864
(new RenderConsoleCommand($this->console))($consoleCommand);
6965
}
7066
}
67+
68+
$this->console
69+
->when(
70+
expression: ! $this->discoveryCache->isValid(),
71+
callback: fn (Console $console) => $console->writeln(PHP_EOL . '<error>Discovery cache invalid. Run discovery:generate to enable discovery caching.</error>')
72+
);
7173
}
7274
}

0 commit comments

Comments
 (0)