diff --git a/README.md b/README.md index 2bb3c6e..4dbb3bb 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,7 @@ -# The PHP framework that gets out of your way. -[![Coverage Status](https://coveralls.io/repos/github/tempestphp/tempest-framework/badge.svg?branch=main)](https://coveralls.io/github/tempestphp/tempest-framework?branch=main) +# tempest/core -Read how to get started with Tempest [here](https://github.com/tempestphp/tempest-docs/blob/main/app/Content/01-getting-started.md). - -Zero config, zero overhead. This is Tempest: +This package ```php -final readonly class BookController -{ - #[Get('/blog')] - public function index() { /* … */ } - - #[Get('/blog/{post}')] - public function show(Post $post) { /* … */ } -} - -final readonly class RssSyncCommand -{ - public function __construct(private Console $console) {} - - #[ConsoleCommand('rss:sync')] - public function __invoke(bool $force = false) { /* … */ } -} -``` - -# Contributing -We welcome contributing to the Tempest framework! We only ask that you take a quick look at our [guidelines](.github/CONTRIBUTING.md) and then head on over to the issues page to see some ways you might help out! +composer require tempest/core +``` \ No newline at end of file diff --git a/app/TestDependency.php b/app/TestDependency.php index 47f3235..fe27e9f 100644 --- a/app/TestDependency.php +++ b/app/TestDependency.php @@ -1,8 +1,12 @@ - - ./tests/Integration - ./tests/Unit diff --git a/src/CoreConfig.php b/src/AppConfig.php similarity index 83% rename from src/CoreConfig.php rename to src/AppConfig.php index 75d22c8..99de059 100644 --- a/src/CoreConfig.php +++ b/src/AppConfig.php @@ -1,16 +1,17 @@ container->get(CoreConfig::class)->discoveryLocations as $discoveryLocation) { + foreach ($this->container->get(AppConfig::class)->discoveryLocations as $discoveryLocation) { $configFiles = glob(PathHelper::make($discoveryLocation->path, 'Config/**.php')); foreach ($configFiles as $configFile) { diff --git a/src/Bootstraps/DiscoveryBootstrap.php b/src/Bootstraps/DiscoveryBootstrap.php index 0980053..cc1550e 100644 --- a/src/Bootstraps/DiscoveryBootstrap.php +++ b/src/Bootstraps/DiscoveryBootstrap.php @@ -8,7 +8,7 @@ use RecursiveIteratorIterator; use ReflectionClass; use SplFileInfo; -use Tempest\CoreConfig; +use Tempest\AppConfig; use Tempest\Container\Container; use Tempest\Discovery\Discovery; use Throwable; @@ -16,27 +16,27 @@ final readonly class DiscoveryBootstrap implements Bootstrap { public function __construct( - private CoreConfig $coreConfig, + private AppConfig $appConfig, private Container $container, ) { } public function boot(): void { - reset($this->coreConfig->discoveryClasses); + reset($this->appConfig->discoveryClasses); - while ($discoveryClass = current($this->coreConfig->discoveryClasses)) { + while ($discoveryClass = current($this->appConfig->discoveryClasses)) { /** @var Discovery $discovery */ $discovery = $this->container->get($discoveryClass); - if ($this->coreConfig->discoveryCache && $discovery->hasCache()) { + if ($this->appConfig->discoveryCache && $discovery->hasCache()) { $discovery->restoreCache($this->container); - next($this->coreConfig->discoveryClasses); + next($this->appConfig->discoveryClasses); continue; } - foreach ($this->coreConfig->discoveryLocations as $discoveryLocation) { + foreach ($this->appConfig->discoveryLocations as $discoveryLocation) { $directories = new RecursiveDirectoryIterator($discoveryLocation->path); $files = new RecursiveIteratorIterator($directories); @@ -69,7 +69,7 @@ public function boot(): void } } - next($this->coreConfig->discoveryClasses); + next($this->appConfig->discoveryClasses); $discovery->storeCache(); } diff --git a/src/Bootstraps/DiscoveryLocationBootstrap.php b/src/Bootstraps/DiscoveryLocationBootstrap.php index da30528..f0877aa 100644 --- a/src/Bootstraps/DiscoveryLocationBootstrap.php +++ b/src/Bootstraps/DiscoveryLocationBootstrap.php @@ -4,32 +4,34 @@ namespace Tempest\Bootstraps; -use Tempest\CoreConfig; +use Tempest\AppConfig; use Tempest\Discovery\DiscoveryLocation; -use Tempest\Kernel; use Tempest\Support\PathHelper; final readonly class DiscoveryLocationBootstrap implements Bootstrap { public function __construct( - private CoreConfig $coreConfig, - private Kernel $kernel, + private AppConfig $appConfig, ) { } public function boot(): void { $discoveredLocations = [ + ...$this->discoverCorePackages(), ...$this->discoverAppNamespaces(), - ...$this->discoverInstalledPackageLocations(), + ...$this->discoverVendorPackages(), ]; - + $this->addDiscoveryLocations($discoveredLocations); } - private function discoverInstalledPackageLocations(): array + /** + * @return DiscoveryLocation[] + */ + private function discoverCorePackages(): array { - $composerPath = PathHelper::make($this->coreConfig->root, 'vendor/composer'); + $composerPath = PathHelper::make($this->appConfig->root, 'vendor/composer'); $installed = $this->loadJsonFile(PathHelper::make($composerPath, 'installed.json')); $packages = $installed['packages'] ?? []; @@ -37,57 +39,84 @@ private function discoverInstalledPackageLocations(): array foreach ($packages as $package) { $packagePath = PathHelper::make($composerPath, $package['install-path'] ?? ''); - $requiresTempest = isset($package['require']['tempest/framework']); - $hasPsr4Namespaces = isset($package['autoload']['psr-4']); $packageName = ($package['name'] ?? null); - $isTempest = $packageName === 'tempest/framework' - || $packageName === 'tempest/core'; - - if (($requiresTempest && $hasPsr4Namespaces) || $isTempest) { - foreach ($package['autoload']['psr-4'] as $namespace => $namespacePath) { - $namespacePath = PathHelper::make($packagePath, $namespacePath); - - $discoveredLocations[] = [ - 'namespace' => $namespace, - 'path' => $namespacePath, - ]; - } + $isTempest = $packageName === 'tempest/framework' || $packageName === 'tempest/core'; + + if (! $isTempest) { + continue; + } + + foreach ($package['autoload']['psr-4'] as $namespace => $namespacePath) { + $namespacePath = PathHelper::make($packagePath, $namespacePath); + + $discoveredLocations[] = new DiscoveryLocation($namespace, $namespacePath); } } return $discoveredLocations; } + /** + * @return DiscoveryLocation[] + */ private function discoverAppNamespaces(): array { - $composer = $this->loadJsonFile(PathHelper::make($this->coreConfig->root, 'composer.json')); + $composer = $this->loadJsonFile(PathHelper::make($this->appConfig->root, 'composer.json')); $namespaceMap = $composer['autoload']['psr-4'] ?? []; $discoveredLocations = []; foreach ($namespaceMap as $namespace => $path) { - $path = PathHelper::make($this->coreConfig->root, $path); + $path = PathHelper::make($this->appConfig->root, $path); - $discoveredLocations[] = [ - 'namespace' => $namespace, - 'path' => $path, - ]; + $discoveredLocations[] = new DiscoveryLocation($namespace, $path); } return $discoveredLocations; } - private function addDiscoveryLocations(array $discoveredLocations): void + /** + * @return DiscoveryLocation[] + */ + private function discoverVendorPackages(): array { - foreach ($discoveredLocations as $location) { - $this->coreConfig->discoveryLocations = [new DiscoveryLocation(...$location), ...$this->coreConfig->discoveryLocations]; + $composerPath = PathHelper::make($this->appConfig->root, 'vendor/composer'); + $installed = $this->loadJsonFile(PathHelper::make($composerPath, 'installed.json')); + $packages = $installed['packages'] ?? []; + + $discoveredLocations = []; + + foreach ($packages as $package) { + $packagePath = PathHelper::make($composerPath, $package['install-path'] ?? ''); + $requiresTempest = isset($package['require']['tempest/framework']) || isset($package['require']['tempest/core']); + $hasPsr4Namespaces = isset($package['autoload']['psr-4']); + + if (! ($requiresTempest && $hasPsr4Namespaces)) { + continue; + } + + foreach ($package['autoload']['psr-4'] as $namespace => $namespacePath) { + $path = PathHelper::make($packagePath, $namespacePath); + + $discoveredLocations[] = new DiscoveryLocation($namespace, $path); + } } + + return $discoveredLocations; + } + + private function addDiscoveryLocations(array $discoveredLocations): void + { + $this->appConfig->discoveryLocations = [ + ...$discoveredLocations, + ...$this->appConfig->discoveryLocations, + ]; } private function loadJsonFile(string $path): array { if (! is_file($path)) { - $relativePath = str_replace($this->coreConfig->root, '.', $path); + $relativePath = str_replace($this->appConfig->root, '.', $path); throw new BootstrapException(sprintf('Could not locate %s, try running "composer install"', $relativePath)); } diff --git a/src/Container/GenericContainer.php b/src/Container/GenericContainer.php index dab5c5c..819ff6f 100644 --- a/src/Container/GenericContainer.php +++ b/src/Container/GenericContainer.php @@ -11,9 +11,9 @@ use ReflectionNamedType; use ReflectionParameter; use ReflectionUnionType; -use Tempest\Support\Reflection\Attributes; use Tempest\Container\Exceptions\CannotAutowireException; use Tempest\Container\Exceptions\CannotInstantiateDependencyException; +use Tempest\Support\Reflection\Attributes; use Throwable; final class GenericContainer implements Container @@ -176,7 +176,7 @@ private function initializerFor(string $className): null|Initializer|DynamicInit ) { return null; } - + if ($initializerClass = $this->initializers[$className] ?? null) { return $this->resolve($initializerClass); } diff --git a/src/Discovery/DiscoveryDiscovery.php b/src/Discovery/DiscoveryDiscovery.php index bdd0c61..11a243e 100644 --- a/src/Discovery/DiscoveryDiscovery.php +++ b/src/Discovery/DiscoveryDiscovery.php @@ -5,7 +5,7 @@ namespace Tempest\Discovery; use ReflectionClass; -use Tempest\CoreConfig; +use Tempest\AppConfig; use Tempest\Container\Container; final readonly class DiscoveryDiscovery implements Discovery @@ -13,7 +13,7 @@ public const CACHE_PATH = __DIR__ . '/discovery-discovery.cache.php'; public function __construct( - private CoreConfig $coreConfig, + private AppConfig $appConfig, ) { } @@ -27,7 +27,7 @@ public function discover(ReflectionClass $class): void return; } - $this->coreConfig->discoveryClasses[] = $class->getName(); + $this->appConfig->discoveryClasses[] = $class->getName(); } public function hasCache(): bool @@ -37,14 +37,14 @@ public function hasCache(): bool public function storeCache(): void { - file_put_contents(self::CACHE_PATH, serialize($this->coreConfig->discoveryClasses)); + file_put_contents(self::CACHE_PATH, serialize($this->appConfig->discoveryClasses)); } public function restoreCache(Container $container): void { $discoveryClasses = unserialize(file_get_contents(self::CACHE_PATH)); - $this->coreConfig->discoveryClasses = $discoveryClasses; + $this->appConfig->discoveryClasses = $discoveryClasses; } public function destroyCache(): void diff --git a/src/Environment.php b/src/Environment.php new file mode 100644 index 0000000..c384c63 --- /dev/null +++ b/src/Environment.php @@ -0,0 +1,20 @@ +get( $bootstrap, kernel: $this, - coreConfig: $this->coreConfig, + appConfig: $this->appConfig, )->boot(); } @@ -45,7 +45,7 @@ private function createContainer(): Container GenericContainer::setInstance($container); $container - ->config($this->coreConfig) + ->config($this->appConfig) ->singleton(self::class, fn () => $this) ->singleton(Container::class, fn () => $container); diff --git a/src/Support/PathHelper.php b/src/Support/PathHelper.php index 7653ba5..956b52d 100644 --- a/src/Support/PathHelper.php +++ b/src/Support/PathHelper.php @@ -1,5 +1,7 @@ init(); $this->assertInstanceOf(Container::class, $container); - $coreConfig = $container->get(CoreConfig::class); + $appConfig = $container->get(AppConfig::class); - $this->assertCount(2, $coreConfig->discoveryClasses); - $this->assertSame(DiscoveryDiscovery::class, $coreConfig->discoveryClasses[0]); - $this->assertSame(InitializerDiscovery::class, $coreConfig->discoveryClasses[1]); + $this->assertCount(2, $appConfig->discoveryClasses); + $this->assertSame(DiscoveryDiscovery::class, $appConfig->discoveryClasses[0]); + $this->assertSame(InitializerDiscovery::class, $appConfig->discoveryClasses[1]); - $this->assertCount(2, $coreConfig->discoveryLocations); - $this->assertSame('Tempest\\', $coreConfig->discoveryLocations[0]->namespace); - $this->assertSame('App\\', $coreConfig->discoveryLocations[1]->namespace); + $this->assertCount(2, $appConfig->discoveryLocations); + $this->assertSame('Tempest\\', $appConfig->discoveryLocations[0]->namespace); + $this->assertSame('App\\', $appConfig->discoveryLocations[1]->namespace); $test = $container->get(TestDependency::class);