Skip to content

Commit 92a31c3

Browse files
authored
feat(core): partial discovery loading (#1737)
1 parent 82fd04d commit 92a31c3

File tree

7 files changed

+136
-27
lines changed

7 files changed

+136
-27
lines changed

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@
248248
"fmt": "vendor/bin/mago fmt",
249249
"lint:fix": "vendor/bin/mago lint --fix --format-after-fix",
250250
"style": "composer fmt && composer lint:fix",
251+
"test": "composer phpunit",
251252
"lint": "vendor/bin/mago lint --potentially-unsafe --minimum-fail-level=note",
252253
"phpstan": "vendor/bin/phpstan analyse src tests --memory-limit=1G",
253254
"rector": "vendor/bin/rector process --no-ansi",

packages/core/src/Commands/DiscoveryGenerateCommand.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,11 @@ public function __construct(
3030
private AppConfig $appConfig,
3131
) {}
3232

33-
#[ConsoleCommand(name: 'discovery:generate', description: 'Compile and cache all discovery according to the configured discovery caching strategy')]
33+
#[ConsoleCommand(
34+
name: 'discovery:generate',
35+
description: 'Compile and cache all discovery according to the configured discovery caching strategy',
36+
aliases: ['d:g'],
37+
)]
3438
public function __invoke(): void
3539
{
3640
$strategy = DiscoveryCacheStrategy::make(env('DISCOVERY_CACHE', default: $this->appConfig->environment->isProduction()));
@@ -59,7 +63,6 @@ public function generateDiscoveryCache(DiscoveryCacheStrategy $strategy, Closure
5963
$kernel = $this->resolveKernel();
6064

6165
$loadDiscoveryClasses = new LoadDiscoveryClasses(
62-
kernel: $kernel,
6366
container: $kernel->container,
6467
discoveryConfig: $kernel->container->get(DiscoveryConfig::class),
6568
discoveryCache: $this->discoveryCache,

packages/core/src/FrameworkKernel.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,10 @@ public function loadDiscoveryLocations(): self
168168
public function loadDiscovery(): self
169169
{
170170
$this->container->addInitializer(DiscoveryCacheInitializer::class);
171-
$this->container->invoke(LoadDiscoveryClasses::class);
171+
$this->container->invoke(
172+
LoadDiscoveryClasses::class,
173+
discoveryLocations: $this->discoveryLocations,
174+
);
172175

173176
return $this;
174177
}

packages/core/src/Kernel/LoadDiscoveryClasses.php

Lines changed: 52 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -26,52 +26,80 @@ final class LoadDiscoveryClasses
2626
private array $shouldSkipForClass = [];
2727

2828
public function __construct(
29-
private readonly Kernel $kernel,
3029
private readonly Container $container,
3130
private readonly DiscoveryConfig $discoveryConfig,
3231
private readonly DiscoveryCache $discoveryCache,
3332
) {}
3433

35-
public function __invoke(): void
36-
{
37-
$discoveries = $this->build();
34+
/**
35+
* @param class-string<Discovery>[]|null $discoveryClasses
36+
* @param DiscoveryLocation[]|null $discoveryLocations
37+
*/
38+
public function __invoke(
39+
?array $discoveryClasses = null,
40+
?array $discoveryLocations = null,
41+
): void {
42+
$discoveries = $this->build($discoveryClasses, $discoveryLocations);
3843

3944
foreach ($discoveries as $discovery) {
4045
$this->applyDiscovery($discovery);
4146
}
4247
}
4348

44-
/** @return Discovery[] */
45-
public function build(): array
46-
{
47-
// DiscoveryDiscovery needs to be applied before we can build all other discoveries
48-
$discoveryDiscovery = $this->resolveDiscovery(DiscoveryDiscovery::class);
49+
/**
50+
* @param class-string<Discovery>[]|null $discoveryClasses
51+
* @param DiscoveryLocation[]|null $discoveryLocations
52+
* @return Discovery[]
53+
*/
54+
public function build(
55+
?array $discoveryClasses = null,
56+
?array $discoveryLocations = null,
57+
): array {
58+
$kernel = $this->container->get(Kernel::class);
4959

50-
// The first pass over all directories to find all discovery classes
51-
$this->discover([$discoveryDiscovery]);
60+
$discoveryLocations ??= $kernel->discoveryLocations;
5261

53-
// Manually apply DiscoveryDiscovery
54-
$this->applyDiscovery($discoveryDiscovery);
62+
if ($discoveryClasses === null) {
63+
// DiscoveryDiscovery needs to be applied before we can build all other discoveries
64+
$discoveryDiscovery = $this->resolveDiscovery(DiscoveryDiscovery::class);
5565

56-
// Resolve all other discoveries from the container, optionally loading their cache
57-
$discoveries = array_map(
58-
fn (string $discoveryClass) => $this->resolveDiscovery($discoveryClass),
59-
$this->kernel->discoveryClasses,
60-
);
66+
// The first pass over all directories to find all discovery classes
67+
$this->discover([$discoveryDiscovery], $discoveryLocations);
6168

62-
// The second pass over all directories to apply all other discovery classes
63-
$this->discover($discoveries);
69+
// Manually apply DiscoveryDiscovery
70+
$this->applyDiscovery($discoveryDiscovery);
6471

65-
return [$discoveryDiscovery, ...$discoveries];
72+
// Resolve all other discoveries from the container, optionally loading their cache
73+
$discoveries = array_map(
74+
fn (string $discoveryClass) => $this->resolveDiscovery($discoveryClass),
75+
$kernel->discoveryClasses,
76+
);
77+
78+
// The second pass over all directories to apply all other discovery classes
79+
$this->discover($discoveries, $discoveryLocations);
80+
81+
return [$discoveryDiscovery, ...$discoveries];
82+
} else {
83+
// Resolve all manually specified discoveries
84+
$discoveries = array_map(
85+
fn (string $discoveryClass) => $this->resolveDiscovery($discoveryClass),
86+
$discoveryClasses,
87+
);
88+
89+
$this->discover($discoveries, $discoveryLocations);
90+
91+
return $discoveries;
92+
}
6693
}
6794

6895
/**
6996
* Build a list of discovery classes within all registered discovery locations
7097
* @param Discovery[] $discoveries
98+
* @param DiscoveryLocation[]|null $discoveryLocations
7199
*/
72-
private function discover(array $discoveries): void
100+
private function discover(array $discoveries, array $discoveryLocations): void
73101
{
74-
foreach ($this->kernel->discoveryLocations as $location) {
102+
foreach ($discoveryLocations as $location) {
75103
// Skip location based on cache status
76104
if ($this->isLocationCached($location)) {
77105
$cachedForLocation = $this->discoveryCache->restore($location);
@@ -258,7 +286,7 @@ private function shouldSkipDiscoveryForClass(Discovery $discovery, ClassReflecto
258286
return true;
259287
}
260288

261-
// Current discovery was present in the except array, so it shouldn't be skipped
289+
// Current discovery was present in the excepted array, so it shouldn't be skipped
262290
return false;
263291
}
264292

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
namespace Tests\Tempest\Integration\Core\Fixtures;
4+
5+
use Tempest\Discovery\Discovery;
6+
use Tempest\Discovery\DiscoveryLocation;
7+
use Tempest\Discovery\IsDiscovery;
8+
use Tempest\Discovery\SkipDiscovery;
9+
use Tempest\Reflection\ClassReflector;
10+
11+
#[SkipDiscovery]
12+
final class ManualTestDiscovery implements Discovery
13+
{
14+
use IsDiscovery;
15+
16+
private bool $shouldDiscover = false;
17+
18+
public function __construct(
19+
private readonly ManualTestDiscoveryDependency $dependency,
20+
) {}
21+
22+
public function discover(DiscoveryLocation $location, ClassReflector $class): void
23+
{
24+
if ($class->getName() === ManualTestDiscoveryDependency::class) {
25+
$this->shouldDiscover = true;
26+
}
27+
}
28+
29+
public function apply(): void
30+
{
31+
$this->dependency->discovered = $this->shouldDiscover;
32+
}
33+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
namespace Tests\Tempest\Integration\Core\Fixtures;
4+
5+
use Tempest\Container\Singleton;
6+
7+
#[Singleton]
8+
final class ManualTestDiscoveryDependency
9+
{
10+
public bool $discovered = false;
11+
}

tests/Integration/Core/LoadDiscoveryClassesTest.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,16 @@
55
namespace Tests\Tempest\Integration\Core;
66

77
use PHPUnit\Framework\Attributes\Test;
8+
use Tempest\Core\Kernel\LoadDiscoveryClasses;
89
use Tempest\Database\MigratesUp;
910
use Tempest\Database\Migrations\RunnableMigrations;
11+
use Tempest\Discovery\DiscoveryLocation;
1012
use Tests\Tempest\Fixtures\Discovery\HiddenDatabaseMigration;
1113
use Tests\Tempest\Fixtures\Discovery\HiddenMigratableDatabaseMigration;
1214
use Tests\Tempest\Fixtures\GlobalHiddenDiscovery;
1315
use Tests\Tempest\Fixtures\GlobalHiddenPathDiscovery;
16+
use Tests\Tempest\Integration\Core\Fixtures\ManualTestDiscovery;
17+
use Tests\Tempest\Integration\Core\Fixtures\ManualTestDiscoveryDependency;
1418
use Tests\Tempest\Integration\FrameworkIntegrationTestCase;
1519

1620
use function Tempest\get;
@@ -52,4 +56,30 @@ public function do_not_discover_except(): void
5256

5357
$this->assertCount(1, $foundMigrations, 'Expected one hidden migration to be found');
5458
}
59+
60+
#[Test]
61+
public function only_load_specific_discovery_classes(): void
62+
{
63+
/** @var ManualTestDiscoveryDependency $dependency */
64+
$dependency = $this->container->get(ManualTestDiscoveryDependency::class);
65+
$dependency->discovered = false;
66+
67+
/** @var LoadDiscoveryClasses $loadDiscoveryClasses */
68+
$loadDiscoveryClasses = $this->container->get(LoadDiscoveryClasses::class);
69+
70+
$loadDiscoveryClasses(discoveryClasses: [], discoveryLocations: []);
71+
72+
$this->assertFalse($dependency->discovered);
73+
74+
$loadDiscoveryClasses(
75+
discoveryClasses: [
76+
ManualTestDiscovery::class,
77+
],
78+
discoveryLocations: [
79+
new DiscoveryLocation('Tests\Tempest\Integration\Core\Fixtures', __DIR__ . '/Fixtures'),
80+
],
81+
);
82+
83+
$this->assertTrue($dependency->discovered);
84+
}
5585
}

0 commit comments

Comments
 (0)