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

tempestphp / tempest-framework / 14140550176

28 Mar 2025 10:29PM UTC coverage: 80.716% (+1.4%) from 79.334%
14140550176

push

github

web-flow
feat(support): support `$default` on array `first` and `last` methods (#1096)

11 of 12 new or added lines in 2 files covered. (91.67%)

135 existing lines in 16 files now uncovered.

10941 of 13555 relevant lines covered (80.72%)

100.32 hits per line

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

76.24
/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(
707✔
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();
707✔
37
    }
38

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

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

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

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

75
        $this->root = $root;
707✔
76

77
        return $this;
707✔
78
    }
79

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

UNCOV
85
        exit($status);
×
86
    }
87

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

92
        GenericContainer::setInstance($container);
707✔
93

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

96
        return $container;
707✔
97
    }
98

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

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

108
        return $this;
707✔
109
    }
110

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

116
        return $this;
707✔
117
    }
118

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

124
        return $this;
707✔
125
    }
126

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

UNCOV
135
            $message = $error['message'] ?? '';
×
136

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

142
        return $this;
707✔
143
    }
144

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

149
        return $this;
707✔
150
    }
151

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

156
        return $this;
707✔
157
    }
158

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

163
        return $this;
707✔
164
    }
165

166
    public function registerInternalStorage(): self
707✔
167
    {
168
        $path = $this->root . '/vendor/.tempest';
707✔
169

170
        if (! is_dir($path)) {
707✔
171
            mkdir($path, recursive: true);
×
172
        }
173

174
        $this->internalStorage = realpath($path);
707✔
175

176
        return $this;
707✔
177
    }
178

179
    public function finishDeferredTasks(): self
×
180
    {
UNCOV
181
        $this->container->invoke(FinishDeferredTasks::class);
×
182

UNCOV
183
        return $this;
×
184
    }
185

186
    public function event(object $event): self
707✔
187
    {
188
        if (interface_exists(EventBus::class)) {
707✔
189
            $this->container->get(EventBus::class)->dispatch($event);
707✔
190
        }
191

192
        return $this;
707✔
193
    }
194

195
    public function registerEmergencyErrorHandler(): self
707✔
196
    {
197
        $environment = Environment::fromEnv();
707✔
198

199
        // During tests, PHPUnit registers its own error handling.
200
        if ($environment->isTesting()) {
707✔
201
            return $this;
707✔
202
        }
203

204
        // In development, we want to register a developer-friendly error
205
        // handler as soon as possible to catch any kind of exception.
UNCOV
206
        if (PHP_SAPI !== 'cli' && ! $environment->isProduction()) {
×
UNCOV
207
            $whoops = new Run();
×
UNCOV
208
            $whoops->pushHandler(new PrettyPageHandler());
×
UNCOV
209
            $whoops->register();
×
210
        }
211

212
        return $this;
×
213
    }
214

215
    public function registerErrorHandler(): self
707✔
216
    {
217
        $appConfig = $this->container->get(AppConfig::class);
707✔
218

219
        // During tests, PHPUnit registers its own error handling.
220
        if ($appConfig->environment->isTesting()) {
707✔
221
            return $this;
707✔
222
        }
223

224
        // We already have a non-CLI error handler.
UNCOV
225
        if (PHP_SAPI !== 'cli') {
×
UNCOV
226
            return $this;
×
227
        }
228

UNCOV
229
        $handler = $this->container->get(ConsoleErrorHandler::class);
×
UNCOV
230
        set_exception_handler($handler->handleException(...));
×
UNCOV
231
        set_error_handler($handler->handleError(...)); // @phpstan-ignore-line
×
232

UNCOV
233
        return $this;
×
234
    }
235
}
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