Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
brendt committed Apr 5, 2024
1 parent 5ba068f commit 5a4e647
Show file tree
Hide file tree
Showing 16 changed files with 142 additions and 106 deletions.
29 changes: 4 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
@@ -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
```
8 changes: 6 additions & 2 deletions app/TestDependency.php
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
<?php

declare(strict_types=1);

namespace App;

final readonly class TestDependency
{
public function __construct(public string $input) {}
}
public function __construct(public string $input)
{
}
}
4 changes: 3 additions & 1 deletion app/TestDependencyInitializer.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace App;

use Tempest\Container\Container;
Expand All @@ -11,4 +13,4 @@ public function initialize(Container $container): TestDependency
{
return new TestDependency('test');
}
}
}
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"php": "^8.3",
"giggsey/libphonenumber-for-php": "^8.13",
"egulias/email-validator": "^4.0",
"tempest/highlight": "^1.0"
"tempest/highlight": "^1.0",
"symfony/var-exporter": "^7.0"
},
"require-dev": {
"phpunit/phpunit": "^10.2",
Expand All @@ -32,7 +33,6 @@
"csfixer": "vendor/bin/php-cs-fixer fix --allow-risky=yes",
"phpstan": "vendor/bin/phpstan analyse src tests app",
"qa": [
"./tempest discovery:clear",
"composer csfixer",
"composer phpstan",
"composer phpunit"
Expand Down
5 changes: 0 additions & 5 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,6 @@ includes:
- phpstan-baseline.php
- vendor/phpat/phpat/extension.neon
- vendor/spaze/phpstan-disallowed-calls/extension.neon
services:
-
class: Tests\Tempest\Architecture\ArchitectureTest
tags:
- phpat.test
parameters:
level: 6
reportUnmatchedIgnoredErrors: false
Expand Down
3 changes: 0 additions & 3 deletions phpunit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@
displayDetailsOnTestsThatTriggerWarnings="true"
>
<testsuites>
<testsuite name="Integration">
<directory suffix="Test.php">./tests/Integration</directory>
</testsuite>
<testsuite name="Unit">
<directory suffix="Test.php">./tests/Unit</directory>
</testsuite>
Expand Down
12 changes: 7 additions & 5 deletions src/CoreConfig.php → src/AppConfig.php
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
<?php

declare(strict_types=1);

namespace Tempest;

use Tempest\Discovery\DiscoveryDiscovery;

final class CoreConfig
final class AppConfig
{
public function __construct(
public string $root,

public Environment $environment = Environment::LOCAL,
public bool $enableExceptionHandling = false,

public bool $discoveryCache = false,

/** @var class-string[] */
Expand All @@ -27,5 +28,6 @@ public function __construct(
public array $exceptionHandlers = [
// …,
],
) {}
}
) {
}
}
4 changes: 2 additions & 2 deletions src/Bootstraps/ConfigBootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace Tempest\Bootstraps;

use Tempest\CoreConfig;
use Tempest\AppConfig;
use Tempest\Container\Container;
use Tempest\Support\PathHelper;

Expand All @@ -18,7 +18,7 @@ public function __construct(
public function boot(): void
{
// Scan for config files in all discovery locations
foreach ($this->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) {
Expand Down
16 changes: 8 additions & 8 deletions src/Bootstraps/DiscoveryBootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,35 +8,35 @@
use RecursiveIteratorIterator;
use ReflectionClass;
use SplFileInfo;
use Tempest\CoreConfig;
use Tempest\AppConfig;
use Tempest\Container\Container;
use Tempest\Discovery\Discovery;
use Throwable;

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);

Expand Down Expand Up @@ -69,7 +69,7 @@ public function boot(): void
}
}

next($this->coreConfig->discoveryClasses);
next($this->appConfig->discoveryClasses);

$discovery->storeCache();
}
Expand Down
93 changes: 61 additions & 32 deletions src/Bootstraps/DiscoveryLocationBootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,90 +4,119 @@

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'] ?? [];

$discoveredLocations = [];

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));
}
Expand Down
4 changes: 2 additions & 2 deletions src/Container/GenericContainer.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -176,7 +176,7 @@ private function initializerFor(string $className): null|Initializer|DynamicInit
) {
return null;
}

if ($initializerClass = $this->initializers[$className] ?? null) {
return $this->resolve($initializerClass);
}
Expand Down
Loading

0 comments on commit 5a4e647

Please sign in to comment.