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

tempestphp / tempest-framework / 14094879667

26 Mar 2025 10:42PM UTC coverage: 79.282% (-0.05%) from 79.334%
14094879667

Pull #1084

github

web-flow
Merge 0e17e2ffe into 6af05d563
Pull Request #1084: feat(router): introduce response processors

40 of 59 new or added lines in 7 files covered. (67.8%)

17 existing lines in 1 file now uncovered.

10550 of 13307 relevant lines covered (79.28%)

91.36 hits per line

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

70.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(
664✔
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();
664✔
38
    }
39

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

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

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

UNCOV
72
        exit($status);
×
73
    }
74

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

79
        GenericContainer::setInstance($container);
664✔
80

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

83
        return $container;
664✔
84
    }
85

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

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

95
        return $this;
664✔
96
    }
97

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

103
        return $this;
664✔
104
    }
105

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

111
        return $this;
664✔
112
    }
113

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

UNCOV
122
            $message = $error['message'] ?? '';
×
123

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

129
        return $this;
664✔
130
    }
131

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

136
        return $this;
664✔
137
    }
138

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

143
        return $this;
664✔
144
    }
145

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

150
        return $this;
664✔
151
    }
152

153
    public function registerErrorHandler(): self
664✔
154
    {
155
        $appConfig = $this->container->get(AppConfig::class);
664✔
156

157
        if ($appConfig->environment->isTesting()) {
664✔
158
            return $this;
664✔
159
        }
160

UNCOV
161
        if (PHP_SAPI === 'cli') {
×
UNCOV
162
            $handler = $this->container->get(ConsoleErrorHandler::class);
×
UNCOV
163
            set_exception_handler($handler->handleException(...));
×
UNCOV
164
            set_error_handler($handler->handleError(...)); // @phpstan-ignore-line
×
165
        } elseif ($appConfig->environment->isProduction()) {
×
UNCOV
166
            $handler = $this->container->get(HttpProductionErrorHandler::class);
×
167
            set_exception_handler($handler->handleException(...));
×
UNCOV
168
            set_error_handler($handler->handleError(...)); // @phpstan-ignore-line
×
169
        }
170

UNCOV
171
        return $this;
×
172
    }
173

174
    public function registerInternalStorage(): self
664✔
175
    {
176
        $path = $this->root . '/vendor/.tempest';
664✔
177

178
        if (! is_dir($path)) {
664✔
UNCOV
179
            mkdir($path, recursive: true);
×
180
        }
181

182
        $this->internalStorage = realpath($path);
664✔
183

184
        return $this;
664✔
185
    }
186

UNCOV
187
    public function finishDeferredTasks(): self
×
188
    {
UNCOV
189
        $this->container->invoke(FinishDeferredTasks::class);
×
190

191
        return $this;
×
192
    }
193

194
    public function event(object $event): self
664✔
195
    {
196
        if (interface_exists(EventBus::class)) {
664✔
197
            $this->container->get(EventBus::class)->dispatch($event);
664✔
198
        }
199

200
        return $this;
664✔
201
    }
202

203
    public function registerKernelErrorHandler(): self
664✔
204
    {
205
        $environment = Environment::fromEnv();
664✔
206

207
        if ($environment->isTesting()) {
664✔
208
            return $this;
664✔
209
        }
210

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') {
×
216
            $whoops = new Run();
×
217
            $whoops->pushHandler(new PrettyPageHandler());
×
218
            $whoops->register();
×
219
        }
220

UNCOV
221
        return $this;
×
222
    }
223
}
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