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

nette / application / 27919019709

21 Jun 2026 10:08PM UTC coverage: 84.111% (+0.05%) from 84.059%
27919019709

push

github

dg
phpstan fix

2038 of 2423 relevant lines covered (84.11%)

0.84 hits per line

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

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

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

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

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

89

90
        /**
91
         * Creates request from the current HTTP request via router.
92
         * @throws BadRequestException
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
        /**
119
         * Processes a presenter request and dispatches the response.
120
         */
121
        public function processRequest(Request $request): void
1✔
122
        {
123
                process:
124
                if (count($this->requests) > $this->maxLoop) {
1✔
125
                        throw new ApplicationException('Too many loops detected in application life cycle.');
1✔
126
                }
127

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

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

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

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

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

155
                Arrays::invoke($this->onResponse, $this, $response);
1✔
156
                $response->send($this->httpRequest, $this->httpResponse);
1✔
157
        }
1✔
158

159

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

169
                if ($errorPresenter === null) {
1✔
170
                        return null;
1✔
171
                }
172

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

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

185

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

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

197

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

207

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

216

217
        /********************* services ****************d*g**/
218

219

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

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