Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
69a7c7b
Add registry
brendt Mar 11, 2026
086545f
Move things around, decouple things, make Aidan happy.
brendt Mar 11, 2026
e1c00bf
QA
brendt Mar 11, 2026
ad30d43
QA
brendt Mar 11, 2026
e17108b
Moving classes a-round, oh yeah, a-round.
brendt Mar 11, 2026
fccc5c0
Aidan's gonna love reading these commit messages
brendt Mar 11, 2026
8162e91
Making my code uglier, to make Aidan's code work.
brendt Mar 11, 2026
1eb5cff
The atrocity is real
brendt Mar 11, 2026
2e1ca4c
Even cleanup can't save us…
brendt Mar 11, 2026
c76c35f
The horror…
brendt Mar 11, 2026
d50348e
Cleaning up, as far as possible
brendt Mar 11, 2026
57b2c2a
Wip. Yes, it's wip
brendt Mar 11, 2026
e5e319b
Moarrr WIP — but actually getting somewhere. Aidan's gonna be so happy
brendt Mar 11, 2026
66c4a77
Forgot to ran QA. Always a good idea
brendt Mar 11, 2026
8ae0a81
Did we actually… make it green?
brendt Mar 11, 2026
6410671
RectOOOOOOOOOOOOOAAAAAArrr
brendt Mar 11, 2026
7852936
We did not make it green :(
brendt Mar 11, 2026
dda1183
Ok now it'll be green!
brendt Mar 11, 2026
f616bdc
No but now for real
brendt Mar 11, 2026
1d62fc6
Improvement, yes?
brendt Mar 12, 2026
45b46b3
Update docs
brendt Mar 12, 2026
5876ebb
Remove container dependency from support
brendt Mar 12, 2026
029fdf1
Update docs
brendt Mar 12, 2026
00b9ca9
Fix deps
brendt Mar 12, 2026
a7ab796
Making it faster, better, stronger
brendt Mar 12, 2026
223fb13
Docs
brendt Mar 12, 2026
ba94673
Docs, of course
brendt Mar 12, 2026
d991965
QA, of course
brendt Mar 12, 2026
a606c0f
Testing
brendt Mar 12, 2026
1b7b3d3
Fixing testing
brendt Mar 12, 2026
d1f28b1
Fixing testing
brendt Mar 12, 2026
5ff8c02
QA
brendt Mar 12, 2026
1880b21
QA
brendt Mar 12, 2026
c283ecf
Rector wip
brendt Mar 12, 2026
d49074c
Rector wip
brendt Mar 12, 2026
47c0b6e
Fix cache path
brendt Mar 12, 2026
317215b
wip
brendt Mar 12, 2026
7a3a7b9
Fix namespace
brendt Mar 12, 2026
dd9d8d5
Remove registry
brendt Mar 12, 2026
16ffd0c
WIP
brendt Mar 12, 2026
0bf1bfe
WIP
brendt Mar 12, 2026
87f754f
WIP
brendt Mar 12, 2026
72fe86a
wip
brendt Mar 12, 2026
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
3 changes: 3 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"psr-discovery/http-factory-implementations": "^1.2",
"psr/cache": "^3.0",
"psr/clock": "^1.0.0",
"psr/container": "^2.0",
"psr/http-client": "^1.0.0",
"psr/http-factory": "^1.0",
"psr/http-message": "^1.0|^2.0",
Expand Down Expand Up @@ -73,6 +74,7 @@
"nesbot/carbon": "^3.8",
"nyholm/psr7": "^1.8",
"patrickbussmann/oauth2-apple": "^0.3",
"php-di/php-di": "^7.0",
"phpat/phpat": "^0.11.0",
"phpbench/phpbench": "^1.4",
"phpstan/phpstan": "2.1.40",
Expand Down Expand Up @@ -217,6 +219,7 @@
"Tempest\\Database\\Tests\\": "packages/database/tests",
"Tempest\\DateTime\\Tests\\": "packages/datetime/tests",
"Tempest\\Debug\\Tests\\": "packages/debug/tests",
"Tempest\\Discovery\\Tests\\": "packages/discovery/tests",
"Tempest\\EventBus\\Tests\\": "packages/event-bus/tests",
"Tempest\\Generation\\Tests\\": "packages/generation/tests",
"Tempest\\HttpClient\\Tests\\": "packages/http-client/tests",
Expand Down
69 changes: 69 additions & 0 deletions docs/1-essentials/05-discovery.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,3 +218,72 @@ Most of Tempest's features are built on top of discovery. The following is a non
- {b`Tempest\Vite\ViteDiscovery`} discovers `*.entrypoint.{ts,js,css}` files and register them as [entrypoints](../2-features/02-asset-bundling.md#entrypoints).
- {b`Tempest\Auth\AccessControl\PolicyDiscovery`} discovers methods annotated with the {b`#[Tempest\Auth\AccessControl\Policy]`} attribute and registers them as [access control policies](../2-features/04-authentication.md#access-control).
- {b`Tempest\Core\InsightsProviderDiscovery`} discovers classes that implement {b`Tempest\Core\InsightsProvider`} and registers them as insights providers, which power the `tempest about` command.

## Discovery as a standalone package

`tempest/discovery` can be used as a standalone package in any application. All it needs is a PSR-11 compliant container.

Start by requiring `tempest/discovery`:

```console
composer require tempest/discovery
```

Next, you can boot discovery:

```php
use Tempest\Discovery\BootDiscovery;
use Tempest\Discovery\DiscoveryConfig;

// $container is any PSR-11 compliant container, already available in your app

new BootDiscovery(
container: $container,
config: DiscoveryConfig::autoload(__DIR__),
)();
```

Whenever this action is run, discovery will find all discovery classes, and run them against all registered locations.

### Manually specify discovery locations

`DiscoveryConfig::autoload()` will scan a given root path and autmatically determine discovery locations by analyzing the composer.json file in that path. If you prefer another way of defining locations to scan, you can manually provide them via `DiscoveryConfig`:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
`DiscoveryConfig::autoload()` will scan a given root path and autmatically determine discovery locations by analyzing the composer.json file in that path. If you prefer another way of defining locations to scan, you can manually provide them via `DiscoveryConfig`:
`DiscoveryConfig::autoload()` will scan a given root path and automatically determine discovery locations by analyzing the composer.json file in that path. If you prefer another way of defining locations to scan, you can manually provide them via `DiscoveryConfig`:


```php
use Tempest\Discovery\DiscoveryConfig;
use Tempest\Discovery\DiscoveryLocation;

$config = new DiscoveryConfig(locations: [
new DiscoveryLocation('App\\', 'src/'),
// …
]);
```

### Config and caching

You can pass config and cache parameters into the `BootDiscovery` action, with these you can exclude files and classes from discovery, as well as config caching behavior:

```php
use Tempest\Discovery\BootDiscovery;
use Tempest\Discovery\DiscoveryCache;
use Tempest\Discovery\DiscoveryCacheStrategy;
use Tempest\Discovery\DiscoveryConfig;

new BootDiscovery(
container: $container,
config: DiscoveryConfig::autoload(__DIR__)
->skipClasses(
\App\Foo::class,
\Tempest\Container\AutowireDiscovery::class
)
->skipPaths(
__DIR__ . '/../vendor/tempest/support'
),
cache: new DiscoveryCache(
strategy: DiscoveryCacheStrategy::PARTIAL,
pool: new PhpFilesAdapter(
directory: __DIR__ . '/.cache/discovery'
)
),
)();
```
2 changes: 1 addition & 1 deletion packages/cache/src/Commands/CacheClearCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
use Tempest\Container\Container;
use Tempest\Container\GenericContainer;
use Tempest\Core\ConfigCache;
use Tempest\Core\DiscoveryCache;
use Tempest\Discovery\DiscoveryCache;
use Tempest\Icon\IconCache;
use Tempest\Support\Str;
use Tempest\View\ViewCache;
Expand Down
2 changes: 1 addition & 1 deletion packages/cache/src/Commands/CacheStatusCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
use Tempest\Container\Container;
use Tempest\Container\GenericContainer;
use Tempest\Core\ConfigCache;
use Tempest\Core\DiscoveryCache;
use Tempest\Core\Environment;
use Tempest\Discovery\DiscoveryCache;
use Tempest\Icon\IconCache;
use Tempest\Support\Str;
use Tempest\View\ViewCache;
Expand Down
4 changes: 2 additions & 2 deletions packages/cache/src/InternalCacheInsightsProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
namespace Tempest\Cache;

use Tempest\Core\ConfigCache;
use Tempest\Core\DiscoveryCache;
use Tempest\Core\DiscoveryCacheStrategy;
use Tempest\Core\Insight;
use Tempest\Core\InsightsProvider;
use Tempest\Core\InsightType;
use Tempest\Discovery\DiscoveryCache;
use Tempest\Discovery\DiscoveryCacheStrategy;
use Tempest\Icon\IconCache;
use Tempest\View\ViewCache;

Expand Down
2 changes: 1 addition & 1 deletion packages/console/src/Middleware/OverviewMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
use Tempest\Console\ExitCode;
use Tempest\Console\Initializers\Invocation;
use Tempest\Core\AppConfig;
use Tempest\Core\DiscoveryCache;
use Tempest\Core\Priority;
use Tempest\Discovery\DiscoveryCache;

use function Tempest\Support\arr;
use function Tempest\Support\str;
Expand Down
3 changes: 2 additions & 1 deletion packages/container/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"minimum-stability": "dev",
"require": {
"php": "^8.5",
"tempest/reflection": "3.x-dev"
"tempest/reflection": "3.x-dev",
"psr/container": "^2.0"
},
"autoload": {
"files": [
Expand Down
3 changes: 2 additions & 1 deletion packages/container/src/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@

namespace Tempest\Container;

use Psr\Container\ContainerInterface;
use Tempest\Reflection\ClassReflector;
use Tempest\Reflection\FunctionReflector;
use Tempest\Reflection\MethodReflector;
use UnitEnum;

interface Container
interface Container extends ContainerInterface
{
public function register(string $className, callable $definition): self;

Expand Down
10 changes: 9 additions & 1 deletion packages/container/src/GenericContainer.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@

use ArrayIterator;
use Closure;
use Psr\Container\ContainerInterface;
use ReflectionFunction;
use Tempest\Container\Exceptions\CircularDependencyEncountered;
use Tempest\Container\Exceptions\DecoratorDidNotImplementInterface;
use Tempest\Container\Exceptions\DependencyCouldNotBeAutowired;
use Tempest\Container\Exceptions\DependencyCouldNotBeInstantiated;
Expand Down Expand Up @@ -40,7 +42,11 @@ public function __construct(
/** @var ArrayIterator<array-key, class-string[]> $decorators */
private(set) ArrayIterator $decorators = new ArrayIterator(),
private(set) ?DependencyChain $chain = null,
) {}
) {
$this->singleton(Container::class, $this);
$this->singleton(ContainerInterface::class, $this);
$this->singleton(GenericContainer::class, $this);
}

public function setDefinitions(array $definitions): self
{
Expand Down Expand Up @@ -172,6 +178,8 @@ public function config(object $config): self
* @template TClassName of object
* @param class-string<TClassName> $className
* @return TClassName
* @throws CircularDependencyEncountered
* @throws TaggedDependencyCouldNotBeResolved
*/
public function get(string $className, null|string|UnitEnum $tag = null, mixed ...$params): object
{
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/Commands/DiscoveryClearCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

use Tempest\Console\Console;
use Tempest\Console\ConsoleCommand;
use Tempest\Core\DiscoveryCache;
use Tempest\Discovery\DiscoveryCache;

if (class_exists(\Tempest\Console\ConsoleCommand::class)) {
final readonly class DiscoveryClearCommand
Expand Down
24 changes: 13 additions & 11 deletions packages/core/src/Commands/DiscoveryGenerateCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,21 @@
use Tempest\Console\HasConsole;
use Tempest\Container\Container;
use Tempest\Container\GenericContainer;
use Tempest\Core\DiscoveryCache;
use Tempest\Core\DiscoveryCacheStrategy;
use Tempest\Core\DiscoveryConfig;
use Tempest\Core\FrameworkKernel;
use Tempest\Core\Kernel;
use Tempest\Core\Kernel\LoadDiscoveryClasses;
use Tempest\Discovery\BootDiscovery;
use Tempest\Discovery\DiscoveryCache;
use Tempest\Discovery\DiscoveryCacheStrategy;
use Tempest\Discovery\DiscoveryConfig;

if (class_exists(\Tempest\Console\ConsoleCommand::class)) {
final readonly class DiscoveryGenerateCommand
{
use HasConsole;

public function __construct(
private Kernel $kernel,
private DiscoveryConfig $discoveryConfig,
private FrameworkKernel $kernel,
private DiscoveryCache $discoveryCache,
) {}

Expand Down Expand Up @@ -58,15 +59,15 @@ public function generateDiscoveryCache(DiscoveryCacheStrategy $strategy, Closure
{
$kernel = $this->resolveKernel();

$loadDiscoveryClasses = new LoadDiscoveryClasses(
$bootDiscovery = new BootDiscovery(
container: $kernel->container,
discoveryConfig: $kernel->container->get(DiscoveryConfig::class),
discoveryCache: $this->discoveryCache,
config: $kernel->container->get(DiscoveryConfig::class),
cache: $this->discoveryCache,
);

$discoveries = $loadDiscoveryClasses->build();
$discoveries = $bootDiscovery->build();

foreach ($this->kernel->discoveryLocations as $location) {
foreach ($this->discoveryConfig->locations as $location) {
$this->discoveryCache->store($location, $discoveries);
$log($location->path);
}
Expand All @@ -78,10 +79,11 @@ public function resolveKernel(): Kernel
{
$container = new GenericContainer();
$container->singleton(Container::class, $container);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
$container->singleton(Container::class, $container);

new GenericContainer(); already registers Container::class (and a couple others) as singletons.

$container->singleton(DiscoveryConfig::class, $this->discoveryConfig);

return new FrameworkKernel(
root: $this->kernel->root,
discoveryLocations: $this->kernel->discoveryLocations,
discoveryLocations: $this->kernel->discoveryConfig->locations,
container: $container,
)
->registerKernel()
Expand Down
18 changes: 9 additions & 9 deletions packages/core/src/Commands/DiscoveryStatusCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
use Tempest\Console\Console;
use Tempest\Console\ConsoleArgument;
use Tempest\Console\ConsoleCommand;
use Tempest\Core\DiscoveryCache;
use Tempest\Core\DiscoveryCacheStrategy;
use Tempest\Core\Kernel;
use Tempest\Discovery\DiscoveryCache;
use Tempest\Discovery\DiscoveryCacheStrategy;
use Tempest\Discovery\DiscoveryConfig;
use Tempest\Support\Filesystem;

use function Tempest\root_path;
Expand All @@ -20,20 +20,20 @@
{
public function __construct(
private Console $console,
private Kernel $kernel,
private DiscoveryConfig $discoveryConfig,
private DiscoveryCache $discoveryCache,
) {}

#[ConsoleCommand(name: 'discovery:status', description: 'Lists all discovery locations and discovery classes')]
#[ConsoleCommand(name: 'discovery:status', description: 'Lists all discovery locations and discovery classes', aliases: ['d:s'])]
public function __invoke(
#[ConsoleArgument(description: 'Prints discovery classes', aliases: ['c'])]
bool $showClasses = false,
#[ConsoleArgument(description: 'Prints discovery locations', aliases: ['l'])]
bool $showLocations = false,
): void {
$this->console->header('Discovery status');
$this->console->keyValue('Registered locations', (string) count($this->kernel->discoveryLocations));
$this->console->keyValue('Loaded discovery classes', (string) count($this->kernel->discoveryClasses));
$this->console->keyValue('Registered locations', (string) count($this->discoveryConfig->locations));
$this->console->keyValue('Loaded discovery classes', (string) count($this->discoveryConfig->classes));
$this->console->keyValue('Cache', match ($this->discoveryCache->enabled) {
true => '<style="fg-green bold">ENABLED</style>',
false => '<style="fg-gray bold">DISABLED</style>',
Expand All @@ -53,7 +53,7 @@ public function __invoke(
$this->console->header('Discovery classes', subheader: 'These classes are used by Tempest to determine which classes to discover and how to handle them.');
$this->console->writeln();

foreach ($this->kernel->discoveryClasses as $discoveryClass) {
foreach ($this->discoveryConfig->classes as $discoveryClass) {
$this->console->keyValue("<style='fg-gray'>{$discoveryClass}</style>");
}
}
Expand All @@ -62,7 +62,7 @@ public function __invoke(
$this->console->header('Discovery locations', subheader: 'These locations are used by Tempest to discover classes.');
$this->console->writeln();

foreach ($this->kernel->discoveryLocations as $discoveryLocation) {
foreach ($this->discoveryConfig->locations as $discoveryLocation) {
$path = str(Filesystem\normalize_path($discoveryLocation->path))
->replaceStart(root_path(), '.')
->toString();
Expand Down
Loading
Loading