Skip to content

Commit 5ce87e3

Browse files
authored
Merge branch 'main' into feat/console/make-migration-command
2 parents 7aa8456 + 82efd60 commit 5ce87e3

File tree

94 files changed

+1891
-951
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

94 files changed

+1891
-951
lines changed

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@
128128
"src/Tempest/Support/src/Html/functions.php",
129129
"src/Tempest/Support/src/Language/functions.php",
130130
"src/Tempest/Support/src/Namespace/functions.php",
131+
"src/Tempest/Support/src/Path/functions.php",
131132
"src/Tempest/Support/src/Random/functions.php",
132133
"src/Tempest/Support/src/Regex/functions.php",
133134
"src/Tempest/Support/src/Str/functions.php",

phpstan.neon.dist

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,6 @@ parameters:
2020
ignoreErrors:
2121
-
2222
identifier: argument.named
23-
-
24-
message: '#.*#'
25-
path: src/Tempest/Router/src/Exceptions/exception.php
2623
-
2724
message: '#.*exec*#'
2825
path: src/Tempest/Console/src/Terminal/Terminal.php

rector.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,6 @@
5858
EncapsedStringsToSprintfRector::class,
5959
AddArrowFunctionReturnTypeRector::class,
6060
])
61-
->withSkipPath(__DIR__ . '/src/Tempest/Router/src/Exceptions/HttpProductionErrorHandler.php')
62-
->withSkipPath(__DIR__ . '/src/Tempest/Router/src/Exceptions/exception.php')
6361
->withParallel(300, 10, 10)
6462
->withPreparedSets(
6563
codeQuality: false,

src/Tempest/Console/src/Highlight/TempestConsoleLanguage/Injections/FileInjection.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public function parse(string $content, Highlighter $highlighter): ParsedInjectio
2323
$href = $matches['file'];
2424
$exists = realpath($href) !== false;
2525
$file = $exists
26-
? str(realpath($href))->replace('\\', '/')->replaceStart(root_path('/'), '')
26+
? str(realpath($href))->replace('\\', '/')->stripStart(root_path())->stripStart('/')
2727
: $href;
2828

2929
return TerminalStyle::UNDERLINE((string) $file);

src/Tempest/Console/src/Input/MemoryInputBuffer.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,11 @@ private function consumeBuffer(): string
5252

5353
return $next ?? '';
5454
}
55+
56+
public function clear(): self
57+
{
58+
$this->buffer = [];
59+
60+
return $this;
61+
}
5562
}

src/Tempest/Console/src/Testing/ConsoleTester.php

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ final class ConsoleTester
3535

3636
private bool $withPrompting = true;
3737

38+
private (Console&GenericConsole)|null $console = null;
39+
3840
public function __construct(
3941
private readonly Container $container,
4042
) {}
@@ -43,20 +45,26 @@ public function call(string|Closure|array $command, string|array $arguments = []
4345
{
4446
$clone = clone $this;
4547

46-
$memoryOutputBuffer = new MemoryOutputBuffer();
48+
$this->output ??= new MemoryOutputBuffer();
49+
$this->output->clear();
50+
$memoryOutputBuffer = $this->output;
4751
$clone->container->singleton(OutputBuffer::class, $memoryOutputBuffer);
4852

49-
$memoryInputBuffer = new MemoryInputBuffer();
53+
$this->input ??= new MemoryInputBuffer();
54+
$this->input->clear();
55+
$memoryInputBuffer = $this->input;
5056
$clone->container->singleton(InputBuffer::class, $memoryInputBuffer);
5157

52-
$console = new GenericConsole(
58+
$this->console ??= new GenericConsole(
5359
output: $memoryOutputBuffer,
5460
input: $memoryInputBuffer,
5561
highlighter: $clone->container->get(Highlighter::class, 'console'),
5662
executeConsoleCommand: $clone->container->get(ExecuteConsoleCommand::class),
5763
argumentBag: $clone->container->get(ConsoleArgumentBag::class),
5864
);
5965

66+
$console = $this->console;
67+
6068
if ($this->withPrompting === false) {
6169
$console->disablePrompting();
6270
}

src/Tempest/Core/src/Composer.php

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,20 @@
44

55
namespace Tempest\Core;
66

7+
use Tempest\Support\Namespace\Psr4Namespace;
8+
79
use function Tempest\Support\arr;
8-
use function Tempest\Support\path;
10+
use function Tempest\Support\Arr\wrap;
11+
use function Tempest\Support\Path\normalize;
12+
use function Tempest\Support\Str\ensure_ends_with;
13+
use function Tempest\Support\Str\starts_with;
914

1015
final class Composer
1116
{
12-
/** @var array<ComposerNamespace> */
17+
/** @var array<Psr4Namespace> */
1318
public array $namespaces;
1419

15-
public ?ComposerNamespace $mainNamespace = null;
20+
public ?Psr4Namespace $mainNamespace = null;
1621

1722
private string $composerPath;
1823

@@ -25,16 +30,17 @@ public function __construct(
2530

2631
public function load(): self
2732
{
28-
$this->composerPath = path($this->root, 'composer.json')->toString();
33+
$this->composerPath = normalize($this->root, 'composer.json');
2934
$this->composer = $this->loadComposerFile($this->composerPath);
3035
$this->namespaces = arr($this->composer)
3136
->get('autoload.psr-4', default: arr())
32-
->map(fn (string $path, string $namespace) => new ComposerNamespace($namespace, $path))
37+
->map(fn (string $path, string $namespace) => new Psr4Namespace($namespace, $path))
38+
->sortByCallback(fn (Psr4Namespace $ns1, Psr4Namespace $ns2) => strlen($ns1->path) <=> strlen($ns2->path))
3339
->values()
3440
->toArray();
3541

3642
foreach ($this->namespaces as $namespace) {
37-
if (str_starts_with($namespace->path, 'app/') || str_starts_with($namespace->path, 'src/')) {
43+
if (starts_with(ensure_ends_with($namespace->path, '/'), ['app/', 'src/', 'source/', 'lib/'])) {
3844
$this->mainNamespace = $namespace;
3945

4046
break;
@@ -45,16 +51,31 @@ public function load(): self
4551
$this->mainNamespace = $this->namespaces[0];
4652
}
4753

54+
$this->namespaces = arr([
55+
$this->mainNamespace,
56+
...$this->namespaces,
57+
])
58+
->filter()
59+
->unique(fn (Psr4Namespace $ns) => $ns->namespace)
60+
->toArray();
61+
4862
return $this;
4963
}
5064

51-
public function setMainNamespace(ComposerNamespace $namespace): self
65+
public function setMainNamespace(Psr4Namespace $namespace): self
5266
{
5367
$this->mainNamespace = $namespace;
5468

5569
return $this;
5670
}
5771

72+
public function setNamespaces(Psr4Namespace|array $namespaces): self
73+
{
74+
$this->namespaces = wrap($namespaces);
75+
76+
return $this;
77+
}
78+
5879
public function setShellExecutor(ShellExecutor $executor): self
5980
{
6081
$this->executor = $executor;

src/Tempest/Core/src/ComposerNamespace.php

Lines changed: 0 additions & 13 deletions
This file was deleted.

src/Tempest/Core/src/FrameworkKernel.php

Lines changed: 41 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
use Tempest\Core\Kernel\LoadDiscoveryLocations;
1515
use Tempest\Core\ShellExecutors\GenericShellExecutor;
1616
use Tempest\EventBus\EventBus;
17-
use Tempest\Router\Exceptions\HttpProductionErrorHandler;
1817
use Whoops\Handler\PrettyPageHandler;
1918
use Whoops\Run;
2019

@@ -51,8 +50,9 @@ public static function boot(
5150
discoveryLocations: $discoveryLocations,
5251
container: $container,
5352
)
53+
->validateRoot()
5454
->loadEnv()
55-
->registerKernelErrorHandler()
55+
->registerEmergencyErrorHandler()
5656
->registerShutdownFunction()
5757
->registerInternalStorage()
5858
->registerKernel()
@@ -64,6 +64,19 @@ public static function boot(
6464
->event(KernelEvent::BOOTED);
6565
}
6666

67+
public function validateRoot(): self
68+
{
69+
$root = realpath($this->root);
70+
71+
if (! is_dir($root)) {
72+
throw new \RuntimeException('The specified root directory is not valid.');
73+
}
74+
75+
$this->root = $root;
76+
77+
return $this;
78+
}
79+
6780
public function shutdown(int|string $status = ''): never
6881
{
6982
$this->finishDeferredTasks()
@@ -150,27 +163,6 @@ public function loadConfig(): self
150163
return $this;
151164
}
152165

153-
public function registerErrorHandler(): self
154-
{
155-
$appConfig = $this->container->get(AppConfig::class);
156-
157-
if ($appConfig->environment->isTesting()) {
158-
return $this;
159-
}
160-
161-
if (PHP_SAPI === 'cli') {
162-
$handler = $this->container->get(ConsoleErrorHandler::class);
163-
set_exception_handler($handler->handleException(...));
164-
set_error_handler($handler->handleError(...)); // @phpstan-ignore-line
165-
} elseif ($appConfig->environment->isProduction()) {
166-
$handler = $this->container->get(HttpProductionErrorHandler::class);
167-
set_exception_handler($handler->handleException(...));
168-
set_error_handler($handler->handleError(...)); // @phpstan-ignore-line
169-
}
170-
171-
return $this;
172-
}
173-
174166
public function registerInternalStorage(): self
175167
{
176168
$path = $this->root . '/vendor/.tempest';
@@ -200,24 +192,44 @@ public function event(object $event): self
200192
return $this;
201193
}
202194

203-
public function registerKernelErrorHandler(): self
195+
public function registerEmergencyErrorHandler(): self
204196
{
205197
$environment = Environment::fromEnv();
206198

199+
// During tests, PHPUnit registers its own error handling.
207200
if ($environment->isTesting()) {
208201
return $this;
209202
}
210203

211-
if (PHP_SAPI !== 'cli' && $environment->isProduction()) {
212-
$handler = new HttpProductionErrorHandler();
213-
set_exception_handler($handler->handleException(...));
214-
set_error_handler($handler->handleError(...)); // @phpstan-ignore-line
215-
} elseif (PHP_SAPI !== 'cli') {
204+
// In development, we want to register a developer-friendly error
205+
// handler as soon as possible to catch any kind of exception.
206+
if (PHP_SAPI !== 'cli' && ! $environment->isProduction()) {
216207
$whoops = new Run();
217208
$whoops->pushHandler(new PrettyPageHandler());
218209
$whoops->register();
219210
}
220211

221212
return $this;
222213
}
214+
215+
public function registerErrorHandler(): self
216+
{
217+
$appConfig = $this->container->get(AppConfig::class);
218+
219+
// During tests, PHPUnit registers its own error handling.
220+
if ($appConfig->environment->isTesting()) {
221+
return $this;
222+
}
223+
224+
// We already have a non-CLI error handler.
225+
if (PHP_SAPI !== 'cli') {
226+
return $this;
227+
}
228+
229+
$handler = $this->container->get(ConsoleErrorHandler::class);
230+
set_exception_handler($handler->handleException(...));
231+
set_error_handler($handler->handleError(...)); // @phpstan-ignore-line
232+
233+
return $this;
234+
}
223235
}

src/Tempest/Core/src/Kernel.php

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,7 @@
44

55
namespace Tempest\Core;
66

7-
use Dotenv\Dotenv;
8-
use Tempest\Console\Exceptions\ConsoleErrorHandler;
97
use Tempest\Container\Container;
10-
use Tempest\Container\GenericContainer;
11-
use Tempest\Core\Kernel\FinishDeferredTasks;
12-
use Tempest\Core\Kernel\LoadConfig;
13-
use Tempest\Core\Kernel\LoadDiscoveryClasses;
14-
use Tempest\Core\Kernel\LoadDiscoveryLocations;
15-
use Tempest\Core\ShellExecutors\GenericShellExecutor;
16-
use Tempest\EventBus\EventBus;
17-
use Tempest\Router\Exceptions\HttpProductionErrorHandler;
18-
use Whoops\Handler\PrettyPageHandler;
19-
use Whoops\Run;
208

219
interface Kernel
2210
{

0 commit comments

Comments
 (0)