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

nette / application / 20834855301

08 Jan 2026 10:54PM UTC coverage: 84.605% (+1.7%) from 82.856%
20834855301

push

github

dg
added CLAUDE.md

1940 of 2293 relevant lines covered (84.61%)

0.85 hits per line

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

90.79
/src/Application/Application.php
1
<?php
2

3
/**
4
 * This file is part of the Nette Framework (https://nette.org)
5
 * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
6
 */
7

8
declare(strict_types=1);
9

10
namespace Nette\Application;
11

12
use Nette;
13
use Nette\Routing\Router;
14
use Nette\Utils\Arrays;
15
use function count, is_string, str_starts_with, strcasecmp;
16

17

18
/**
19
 * Front Controller.
20
 */
21
class Application
22
{
23
        public int $maxLoop = 20;
24

25
        /** @deprecated exceptions are caught if the error presenter is set */
26
        public bool $catchExceptions = true;
27
        public ?string $errorPresenter = null;
28
        public ?string $error4xxPresenter = null;
29

30
        /** @var array<callable(self): void>  Occurs before the application loads presenter */
31
        public array $onStartup = [];
32

33
        /** @var array<callable(self, ?\Throwable): void>  Occurs before the application shuts down */
34
        public array $onShutdown = [];
35

36
        /** @var array<callable(self, Request): void>  Occurs when a new request is received */
37
        public array $onRequest = [];
38

39
        /** @var array<callable(self, IPresenter): void>  Occurs when a presenter is created */
40
        public array $onPresenter = [];
41

42
        /** @var array<callable(self, Response): void>  Occurs when a new response is ready for dispatch */
43
        public array $onResponse = [];
44

45
        /** @var array<callable(self, \Throwable): void>  Occurs when an unhandled exception occurs in the application */
46
        public array $onError = [];
47

48
        /** @var Request[] */
49
        private array $requests = [];
50
        private ?IPresenter $presenter = null;
51

52

53
        public function __construct(
1✔
54
                private readonly IPresenterFactory $presenterFactory,
55
                private readonly Router $router,
56
                private readonly Nette\Http\IRequest $httpRequest,
57
                private readonly Nette\Http\IResponse $httpResponse,
58
        ) {
59
        }
1✔
60

61

62
        /**
63
         * Dispatch a HTTP request to a front controller.
64
         */
65
        public function run(): void
66
        {
67
                try {
68
                        Arrays::invoke($this->onStartup, $this);
1✔
69
                        $this->processRequest($this->createInitialRequest())
1✔
70
                                ->send($this->httpRequest, $this->httpResponse);
1✔
71
                        Arrays::invoke($this->onShutdown, $this);
1✔
72

73
                } catch (\Throwable $e) {
1✔
74
                        $this->sendHttpCode($e);
1✔
75
                        Arrays::invoke($this->onError, $this, $e);
1✔
76
                        if ($this->catchExceptions && ($req = $this->createErrorRequest($e))) {
1✔
77
                                try {
78
                                        $this->processRequest($req)
1✔
79
                                                ->send($this->httpRequest, $this->httpResponse);
1✔
80
                                        Arrays::invoke($this->onShutdown, $this, $e);
1✔
81
                                        return;
1✔
82

83
                                } catch (\Throwable $e) {
1✔
84
                                        Arrays::invoke($this->onError, $this, $e);
1✔
85
                                }
86
                        }
87

88
                        Arrays::invoke($this->onShutdown, $this, $e);
1✔
89
                        throw $e;
1✔
90
                }
91
        }
1✔
92

93

94
        public function createInitialRequest(): Request
95
        {
96
                $params = $this->router->match($this->httpRequest);
1✔
97
                $presenter = $params[UI\Presenter::PresenterKey] ?? null;
1✔
98

99
                if ($params === null) {
1✔
100
                        throw new BadRequestException('No route for HTTP request.');
1✔
101
                } elseif (!is_string($presenter)) {
1✔
102
                        throw new Nette\InvalidStateException('Missing presenter in route definition.');
×
103
                } elseif (str_starts_with($presenter, 'Nette:') && $presenter !== 'Nette:Micro') {
1✔
104
                        throw new BadRequestException('Invalid request. Presenter is not achievable.');
×
105
                }
106

107
                unset($params[UI\Presenter::PresenterKey]);
1✔
108
                return new Request(
1✔
109
                        $presenter,
1✔
110
                        $this->httpRequest->getMethod(),
1✔
111
                        $params,
112
                        $this->httpRequest->getPost(),
1✔
113
                        $this->httpRequest->getFiles(),
1✔
114
                );
115
        }
116

117

118
        public function processRequest(Request $request): Response
1✔
119
        {
120
                process:
121
                if (count($this->requests) > $this->maxLoop) {
1✔
122
                        throw new ApplicationException('Too many loops detected in application life cycle.');
1✔
123
                }
124

125
                $this->requests[] = $request;
1✔
126
                Arrays::invoke($this->onRequest, $this, $request);
1✔
127

128
                if (
129
                        !$request->isMethod($request::FORWARD)
1✔
130
                        && (!strcasecmp($request->getPresenterName(), (string) $this->errorPresenter)
1✔
131
                                || !strcasecmp($request->getPresenterName(), (string) $this->error4xxPresenter))
1✔
132
                ) {
133
                        throw new BadRequestException('Invalid request. Presenter is not achievable.');
1✔
134
                }
135

136
                try {
137
                        $this->presenter = $this->presenterFactory->createPresenter($request->getPresenterName());
1✔
138
                } catch (InvalidPresenterException $e) {
1✔
139
                        throw count($this->requests) > 1
1✔
140
                                ? $e
×
141
                                : new BadRequestException($e->getMessage(), 0, $e);
1✔
142
                }
143

144
                Arrays::invoke($this->onPresenter, $this, $this->presenter);
1✔
145
                $response = $this->presenter->run(clone $request);
1✔
146

147
                if ($response instanceof Responses\ForwardResponse) {
1✔
148
                        $request = $response->getRequest();
1✔
149
                        goto process;
1✔
150
                }
151

152
                Arrays::invoke($this->onResponse, $this, $response);
1✔
153
                return $response;
1✔
154
        }
155

156

157
        public function createErrorRequest(\Throwable $e): ?Request
1✔
158
        {
159
                $errorPresenter = $e instanceof BadRequestException
1✔
160
                        ? $this->error4xxPresenter ?? $this->errorPresenter
1✔
161
                        : $this->errorPresenter;
1✔
162

163
                if ($errorPresenter === null) {
1✔
164
                        return null;
1✔
165
                }
166

167
                $args = ['exception' => $e, 'previousPresenter' => $this->presenter, 'request' => Arrays::last($this->requests) ?? null];
1✔
168
                if ($this->presenter instanceof UI\Presenter) {
1✔
169
                        try {
170
                                $this->presenter->forward(":$errorPresenter:", $args);
1✔
171
                        } catch (AbortException) {
1✔
172
                                return $this->presenter->getLastCreatedRequest();
1✔
173
                        }
174
                }
175

176
                return new Request($errorPresenter, Request::FORWARD, $args);
1✔
177
        }
178

179

180
        private function sendHttpCode(\Throwable $e): void
1✔
181
        {
182
                if (!$e instanceof BadRequestException && $this->httpResponse instanceof Nette\Http\Response) {
1✔
183
                        $this->httpResponse->warnOnBuffer = false;
×
184
                }
185

186
                if (!$this->httpResponse->isSent()) {
1✔
187
                        $this->httpResponse->setCode($e instanceof BadRequestException ? ($e->getHttpCode() ?: 404) : 500);
1✔
188
                }
189
        }
1✔
190

191

192
        /**
193
         * Returns all processed requests.
194
         * @return Request[]
195
         */
196
        final public function getRequests(): array
197
        {
198
                return $this->requests;
1✔
199
        }
200

201

202
        /**
203
         * Returns current presenter.
204
         */
205
        final public function getPresenter(): ?IPresenter
206
        {
207
                return $this->presenter;
×
208
        }
209

210

211
        /********************* services ****************d*g**/
212

213

214
        /**
215
         * Returns router.
216
         */
217
        public function getRouter(): Router
218
        {
219
                return $this->router;
×
220
        }
221

222

223
        /**
224
         * Returns presenter factory.
225
         */
226
        public function getPresenterFactory(): IPresenterFactory
227
        {
228
                return $this->presenterFactory;
×
229
        }
230
}
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