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

nette / application / 28120116131

24 Jun 2026 05:59PM UTC coverage: 84.766% (+0.2%) from 84.605%
28120116131

push

github

dg
added CLAUDE.md

1992 of 2350 relevant lines covered (84.77%)

0.85 hits per line

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

90.91
/src/Application/Application.php
1
<?php declare(strict_types=1);
1✔
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
namespace Nette\Application;
9

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

15

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

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

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

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

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

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

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

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

46
        /** @var list<Request> */
47
        private array $requests = [];
48
        private ?IPresenter $presenter = null;
49

50

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

59

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

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

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

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

91

92
        /**
93
         * Creates request from the current HTTP request via router.
94
         * @throws BadRequestException
95
         */
96
        public function createInitialRequest(): Request
97
        {
98
                $params = $this->router->match($this->httpRequest);
1✔
99
                $presenter = $params[UI\Presenter::PresenterKey] ?? null;
1✔
100

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

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

119

120
        /**
121
         * Processes a presenter request and dispatches the response.
122
         */
123
        public function processRequest(Request $request): Response
1✔
124
        {
125
                process:
126
                if (count($this->requests) > $this->maxLoop) {
1✔
127
                        throw new ApplicationException('Too many loops detected in application life cycle.');
1✔
128
                }
129

130
                $this->requests[] = $request;
1✔
131
                Arrays::invoke($this->onRequest, $this, $request);
1✔
132

133
                if (
134
                        !$request->isMethod($request::FORWARD)
1✔
135
                        && (!strcasecmp($request->getPresenterName(), (string) $this->errorPresenter)
1✔
136
                                || !strcasecmp($request->getPresenterName(), (string) $this->error4xxPresenter))
1✔
137
                ) {
138
                        throw new BadRequestException('Invalid request. Presenter is not achievable.');
1✔
139
                }
140

141
                try {
142
                        $this->presenter = $this->presenterFactory->createPresenter($request->getPresenterName());
1✔
143
                } catch (InvalidPresenterException $e) {
1✔
144
                        throw count($this->requests) > 1
1✔
145
                                ? $e
×
146
                                : new BadRequestException($e->getMessage(), 0, $e);
1✔
147
                }
148

149
                Arrays::invoke($this->onPresenter, $this, $this->presenter);
1✔
150
                $response = $this->presenter->run(clone $request);
1✔
151

152
                if ($response instanceof Responses\ForwardResponse) {
1✔
153
                        $request = $response->getRequest();
1✔
154
                        goto process;
1✔
155
                }
156

157
                Arrays::invoke($this->onResponse, $this, $response);
1✔
158
                return $response;
1✔
159
        }
160

161

162
        /**
163
         * Creates a forward request to the error presenter, or returns null if not configured.
164
         */
165
        public function createErrorRequest(\Throwable $e): ?Request
1✔
166
        {
167
                $errorPresenter = $e instanceof BadRequestException
1✔
168
                        ? $this->error4xxPresenter ?? $this->errorPresenter
1✔
169
                        : $this->errorPresenter;
1✔
170

171
                if ($errorPresenter === null) {
1✔
172
                        return null;
1✔
173
                }
174

175
                $args = ['exception' => $e, 'previousPresenter' => $this->presenter, 'request' => Arrays::last($this->requests) ?? null];
1✔
176
                if ($this->presenter instanceof UI\Presenter) {
1✔
177
                        try {
178
                                $this->presenter->forward(":$errorPresenter:", $args);
1✔
179
                        } catch (AbortException) {
1✔
180
                                return $this->presenter->getLastCreatedRequest();
1✔
181
                        }
182
                }
183

184
                return new Request($errorPresenter, Request::FORWARD, $args);
1✔
185
        }
186

187

188
        private function sendHttpCode(\Throwable $e): void
1✔
189
        {
190
                if (!$e instanceof BadRequestException && $this->httpResponse instanceof Nette\Http\Response) {
1✔
191
                        $this->httpResponse->warnOnBuffer = false;
×
192
                }
193

194
                if (!$this->httpResponse->isSent()) {
1✔
195
                        $this->httpResponse->setCode($e instanceof BadRequestException ? ($e->getHttpCode() ?: 404) : 500);
1✔
196
                }
197
        }
1✔
198

199

200
        /**
201
         * Returns all processed requests.
202
         * @return list<Request>
203
         */
204
        final public function getRequests(): array
205
        {
206
                return $this->requests;
1✔
207
        }
208

209

210
        /**
211
         * Returns current presenter.
212
         */
213
        final public function getPresenter(): ?IPresenter
214
        {
215
                return $this->presenter;
×
216
        }
217

218

219
        /********************* services ****************d*g**/
220

221

222
        public function getRouter(): Router
223
        {
224
                return $this->router;
×
225
        }
226

227

228
        public function getPresenterFactory(): IPresenterFactory
229
        {
230
                return $this->presenterFactory;
×
231
        }
232
}
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