• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tempestphp / tempest-framework / 14084976444

26 Mar 2025 01:50PM UTC coverage: 79.369% (+0.06%) from 79.311%
14084976444

Pull #1080

github

web-flow
Merge a3c7d8f9b into d1704e8de
Pull Request #1080: refactor: improve path utilities

147 of 158 new or added lines in 16 files covered. (93.04%)

1 existing line in 1 file now uncovered.

10541 of 13281 relevant lines covered (79.37%)

95.55 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

71.3
/src/Tempest/Core/src/FrameworkKernel.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Tempest\Core;
6

7
use Dotenv\Dotenv;
8
use Tempest\Console\Exceptions\ConsoleErrorHandler;
9
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;
20

21
final class FrameworkKernel implements Kernel
22
{
23
    public readonly Container $container;
24

25
    public bool $discoveryCache;
26

27
    public array $discoveryClasses = [];
28

29
    public string $internalStorage;
30

31
    public function __construct(
667✔
32
        public string $root,
33
        /** @var \Tempest\Discovery\DiscoveryLocation[] $discoveryLocations */
34
        public array $discoveryLocations = [],
35
        ?Container $container = null,
36
    ) {
37
        $this->container = $container ?? $this->createContainer();
667✔
38
    }
39

40
    public static function boot(
667✔
41
        string $root,
42
        array $discoveryLocations = [],
43
        ?Container $container = null,
44
    ): self {
45
        if (! defined('TEMPEST_START')) {
667✔
46
            define('TEMPEST_START', value: hrtime(true));
1✔
47
        }
48

49
        return new self(
667✔
50
            root: $root,
667✔
51
            discoveryLocations: $discoveryLocations,
667✔
52
            container: $container,
667✔
53
        )
667✔
54
            ->validateRoot()
667✔
55
            ->loadEnv()
667✔
56
            ->registerKernelErrorHandler()
667✔
57
            ->registerShutdownFunction()
667✔
58
            ->registerInternalStorage()
667✔
59
            ->registerKernel()
667✔
60
            ->loadComposer()
667✔
61
            ->loadDiscoveryLocations()
667✔
62
            ->loadConfig()
667✔
63
            ->loadDiscovery()
667✔
64
            ->registerErrorHandler()
667✔
65
            ->event(KernelEvent::BOOTED);
667✔
66
    }
67

68
    public function validateRoot(): self
667✔
69
    {
70
        $root = realpath($this->root);
667✔
71

72
        if (! is_dir($root)) {
667✔
NEW
73
            throw new \RuntimeException('The specified root directory is not valid.');
×
74
        }
75

76
        $this->root = $root;
667✔
77

78
        return $this;
667✔
79
    }
80

UNCOV
81
    public function shutdown(int|string $status = ''): never
×
82
    {
83
        $this->finishDeferredTasks()
×
84
            ->event(KernelEvent::SHUTDOWN);
×
85

86
        exit($status);
×
87
    }
88

89
    public function createContainer(): Container
667✔
90
    {
91
        $container = new GenericContainer();
667✔
92

93
        GenericContainer::setInstance($container);
667✔
94

95
        $container->singleton(Container::class, fn () => $container);
667✔
96

97
        return $container;
667✔
98
    }
99

100
    public function loadComposer(): self
667✔
101
    {
102
        $composer = new Composer(
667✔
103
            root: $this->root,
667✔
104
            executor: new GenericShellExecutor(),
667✔
105
        )->load();
667✔
106

107
        $this->container->singleton(Composer::class, $composer);
667✔
108

109
        return $this;
667✔
110
    }
111

112
    public function loadEnv(): self
667✔
113
    {
114
        $dotenv = Dotenv::createUnsafeImmutable($this->root);
667✔
115
        $dotenv->safeLoad();
667✔
116

117
        return $this;
667✔
118
    }
119

120
    public function registerKernel(): self
667✔
121
    {
122
        $this->container->singleton(Kernel::class, $this);
667✔
123
        $this->container->singleton(self::class, $this);
667✔
124

125
        return $this;
667✔
126
    }
127

128
    public function registerShutdownFunction(): self
667✔
129
    {
130
        // Fix for classes that don't have a proper PSR-4 namespace,
131
        // they break discovery with an unrecoverable error,
132
        // but you don't know why because PHP simply says "duplicate classname" instead of something reasonable.
133
        register_shutdown_function(function (): void {
667✔
134
            $error = error_get_last();
×
135

136
            $message = $error['message'] ?? '';
×
137

138
            if (str_contains($message, 'Cannot declare class')) {
×
139
                echo 'Does this class have the right namespace?' . PHP_EOL;
×
140
            }
141
        });
667✔
142

143
        return $this;
667✔
144
    }
145

146
    public function loadDiscoveryLocations(): self
667✔
147
    {
148
        $this->container->invoke(LoadDiscoveryLocations::class);
667✔
149

150
        return $this;
667✔
151
    }
152

153
    public function loadDiscovery(): self
667✔
154
    {
155
        $this->container->invoke(LoadDiscoveryClasses::class);
667✔
156

157
        return $this;
667✔
158
    }
159

160
    public function loadConfig(): self
667✔
161
    {
162
        $this->container->invoke(LoadConfig::class);
667✔
163

164
        return $this;
667✔
165
    }
166

167
    public function registerErrorHandler(): self
667✔
168
    {
169
        $appConfig = $this->container->get(AppConfig::class);
667✔
170

171
        if ($appConfig->environment->isTesting()) {
667✔
172
            return $this;
667✔
173
        }
174

175
        if (PHP_SAPI === 'cli') {
×
176
            $handler = $this->container->get(ConsoleErrorHandler::class);
×
177
            set_exception_handler($handler->handleException(...));
×
178
            set_error_handler($handler->handleError(...)); // @phpstan-ignore-line
×
179
        } elseif ($appConfig->environment->isProduction()) {
×
180
            $handler = $this->container->get(HttpProductionErrorHandler::class);
×
181
            set_exception_handler($handler->handleException(...));
×
182
            set_error_handler($handler->handleError(...)); // @phpstan-ignore-line
×
183
        }
184

185
        return $this;
×
186
    }
187

188
    public function registerInternalStorage(): self
667✔
189
    {
190
        $path = $this->root . '/vendor/.tempest';
667✔
191

192
        if (! is_dir($path)) {
667✔
193
            mkdir($path, recursive: true);
×
194
        }
195

196
        $this->internalStorage = realpath($path);
667✔
197

198
        return $this;
667✔
199
    }
200

201
    public function finishDeferredTasks(): self
×
202
    {
203
        $this->container->invoke(FinishDeferredTasks::class);
×
204

205
        return $this;
×
206
    }
207

208
    public function event(object $event): self
667✔
209
    {
210
        if (interface_exists(EventBus::class)) {
667✔
211
            $this->container->get(EventBus::class)->dispatch($event);
667✔
212
        }
213

214
        return $this;
667✔
215
    }
216

217
    public function registerKernelErrorHandler(): self
667✔
218
    {
219
        $environment = Environment::fromEnv();
667✔
220

221
        if ($environment->isTesting()) {
667✔
222
            return $this;
667✔
223
        }
224

225
        if (PHP_SAPI !== 'cli' && $environment->isProduction()) {
×
226
            $handler = new HttpProductionErrorHandler();
×
227
            set_exception_handler($handler->handleException(...));
×
228
            set_error_handler($handler->handleError(...)); // @phpstan-ignore-line
×
229
        } elseif (PHP_SAPI !== 'cli') {
×
230
            $whoops = new Run();
×
231
            $whoops->pushHandler(new PrettyPageHandler());
×
232
            $whoops->register();
×
233
        }
234

235
        return $this;
×
236
    }
237
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc