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

nette / application / 20561245963

28 Dec 2025 11:47PM UTC coverage: 82.276% (-0.2%) from 82.434%
20561245963

push

github

dg
composer: calls Tester with the -C parameter

A pragmatic decision - I do not know how to set extension_dir in a cross-platform way

1959 of 2381 relevant lines covered (82.28%)

0.82 hits per line

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

90.67
/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
                        Arrays::invoke($this->onShutdown, $this);
1✔
71

72
                } catch (\Throwable $e) {
1✔
73
                        $this->sendHttpCode($e);
1✔
74
                        Arrays::invoke($this->onError, $this, $e);
1✔
75
                        if ($this->catchExceptions && ($req = $this->createErrorRequest($e))) {
1✔
76
                                try {
77
                                        $this->processRequest($req);
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
        public function createInitialRequest(): Request
93
        {
94
                $params = $this->router->match($this->httpRequest);
1✔
95
                $presenter = $params[UI\Presenter::PresenterKey] ?? null;
1✔
96

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

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

115

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

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

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

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

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

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

150
                Arrays::invoke($this->onResponse, $this, $response);
1✔
151
                $response->send($this->httpRequest, $this->httpResponse);
1✔
152
        }
1✔
153

154

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

161
                if ($errorPresenter === null) {
1✔
162
                        return null;
1✔
163
                }
164

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

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

177

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

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

189

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

199

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

208

209
        /********************* services ****************d*g**/
210

211

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

220

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