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

tempestphp / tempest-framework / 14130122303

28 Mar 2025 01:33PM UTC coverage: 80.66%. First build
14130122303

Pull #1084

github

web-flow
Merge bc427638e into ed6f85cc6
Pull Request #1084: feat(router): introduce response processors

48 of 79 new or added lines in 9 files covered. (60.76%)

10898 of 13511 relevant lines covered (80.66%)

96.22 hits per line

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

75.53
/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 Whoops\Handler\PrettyPageHandler;
18
use Whoops\Run;
19

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

24
    public bool $discoveryCache;
25

26
    public array $discoveryClasses = [];
27

28
    public string $internalStorage;
29

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

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

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

66
    public function shutdown(int|string $status = ''): never
×
67
    {
68
        $this->finishDeferredTasks()
×
69
            ->event(KernelEvent::SHUTDOWN);
×
70

71
        exit($status);
×
72
    }
73

74
    public function createContainer(): Container
703✔
75
    {
76
        $container = new GenericContainer();
703✔
77

78
        GenericContainer::setInstance($container);
703✔
79

80
        $container->singleton(Container::class, fn () => $container);
703✔
81

82
        return $container;
703✔
83
    }
84

85
    public function loadComposer(): self
703✔
86
    {
87
        $composer = new Composer(
703✔
88
            root: $this->root,
703✔
89
            executor: new GenericShellExecutor(),
703✔
90
        )->load();
703✔
91

92
        $this->container->singleton(Composer::class, $composer);
703✔
93

94
        return $this;
703✔
95
    }
96

97
    public function loadEnv(): self
703✔
98
    {
99
        $dotenv = Dotenv::createUnsafeImmutable($this->root);
703✔
100
        $dotenv->safeLoad();
703✔
101

102
        return $this;
703✔
103
    }
104

105
    public function registerKernel(): self
703✔
106
    {
107
        $this->container->singleton(Kernel::class, $this);
703✔
108
        $this->container->singleton(self::class, $this);
703✔
109

110
        return $this;
703✔
111
    }
112

113
    public function registerShutdownFunction(): self
703✔
114
    {
115
        // Fix for classes that don't have a proper PSR-4 namespace,
116
        // they break discovery with an unrecoverable error,
117
        // but you don't know why because PHP simply says "duplicate classname" instead of something reasonable.
118
        register_shutdown_function(function (): void {
703✔
119
            $error = error_get_last();
×
120

121
            $message = $error['message'] ?? '';
×
122

123
            if (str_contains($message, 'Cannot declare class')) {
×
124
                echo 'Does this class have the right namespace?' . PHP_EOL;
×
125
            }
126
        });
703✔
127

128
        return $this;
703✔
129
    }
130

131
    public function loadDiscoveryLocations(): self
703✔
132
    {
133
        $this->container->invoke(LoadDiscoveryLocations::class);
703✔
134

135
        return $this;
703✔
136
    }
137

138
    public function loadDiscovery(): self
703✔
139
    {
140
        $this->container->invoke(LoadDiscoveryClasses::class);
703✔
141

142
        return $this;
703✔
143
    }
144

145
    public function loadConfig(): self
703✔
146
    {
147
        $this->container->invoke(LoadConfig::class);
703✔
148

149
        return $this;
703✔
150
    }
151

152
    public function registerInternalStorage(): self
703✔
153
    {
154
        $path = $this->root . '/vendor/.tempest';
703✔
155

156
        if (! is_dir($path)) {
703✔
157
            mkdir($path, recursive: true);
×
158
        }
159

160
        $this->internalStorage = realpath($path);
703✔
161

162
        return $this;
703✔
163
    }
164

165
    public function finishDeferredTasks(): self
×
166
    {
167
        $this->container->invoke(FinishDeferredTasks::class);
×
168

169
        return $this;
×
170
    }
171

172
    public function event(object $event): self
703✔
173
    {
174
        if (interface_exists(EventBus::class)) {
703✔
175
            $this->container->get(EventBus::class)->dispatch($event);
703✔
176
        }
177

178
        return $this;
703✔
179
    }
180

181
    public function registerEmergencyErrorHandler(): self
703✔
182
    {
183
        $environment = Environment::fromEnv();
703✔
184

185
        // During tests, PHPUnit registers its own error handling.
186
        if ($environment->isTesting()) {
703✔
187
            return $this;
703✔
188
        }
189

190
        // In development, we want to register a developer-friendly error
191
        // handler as soon as possible to catch any kind of exception.
NEW
192
        if (PHP_SAPI !== 'cli' && ! $environment->isProduction()) {
×
193
            $whoops = new Run();
×
194
            $whoops->pushHandler(new PrettyPageHandler());
×
195
            $whoops->register();
×
196
        }
197

198
        return $this;
×
199
    }
200

201
    public function registerErrorHandler(): self
703✔
202
    {
203
        $appConfig = $this->container->get(AppConfig::class);
703✔
204

205
        // During tests, PHPUnit registers its own error handling.
206
        if ($appConfig->environment->isTesting()) {
703✔
207
            return $this;
703✔
208
        }
209

210
        // We already have a non-CLI error handler.
NEW
211
        if (PHP_SAPI !== 'cli') {
×
NEW
212
            return $this;
×
213
        }
214

NEW
215
        $handler = $this->container->get(ConsoleErrorHandler::class);
×
NEW
216
        set_exception_handler($handler->handleException(...));
×
NEW
217
        set_error_handler($handler->handleError(...)); // @phpstan-ignore-line
×
218

NEW
219
        return $this;
×
220
    }
221
}
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