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

nette / bootstrap / 25406589236

05 May 2026 10:49PM UTC coverage: 91.156% (+0.06%) from 91.096%
25406589236

push

github

dg
made static analysis mandatory

134 of 147 relevant lines covered (91.16%)

0.91 hits per line

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

90.7
/src/Bootstrap/Configurator.php
1
<?php declare(strict_types=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\Bootstrap;
9

10
use Composer\Autoload\ClassLoader;
11
use Composer\InstalledVersions;
12
use Latte;
13
use Nette;
14
use Nette\DI;
15
use Nette\DI\Definitions\Statement;
16
use Tracy;
17
use function in_array, is_array, is_string;
18
use const PHP_RELEASE_VERSION, PHP_SAPI, PHP_VERSION_ID;
19

20

21
/**
22
 * Initial system DI container generator.
23
 */
24
class Configurator
25
{
26
        public const CookieSecret = 'nette-debug';
27

28
        /** @deprecated  use Configurator::CookieSecret */
29
        public const COOKIE_SECRET = self::CookieSecret;
30

31

32
        /** @var array<callable(self, DI\Compiler): void>  Occurs after the compiler is created */
33
        public array $onCompile = [];
34

35
        /** @var array<string, class-string<DI\CompilerExtension>|array{class-string<DI\CompilerExtension>, list<mixed>}> */
36
        public array $defaultExtensions = [
37
                'application' => [Nette\Bridges\ApplicationDI\ApplicationExtension::class, ['%debugMode%', ['%appDir%'], '%tempDir%/cache/nette.application']],
38
                'assets' => [Nette\Bridges\AssetsDI\DIExtension::class, ['%baseUrl%', '%wwwDir%', '%debugMode%']],
39
                'cache' => [Nette\Bridges\CacheDI\CacheExtension::class, ['%tempDir%/cache']],
40
                'constants' => Extensions\ConstantsExtension::class,
41
                'database' => [Nette\Bridges\DatabaseDI\DatabaseExtension::class, ['%debugMode%']],
42
                'decorator' => Nette\DI\Extensions\DecoratorExtension::class,
43
                'di' => [Nette\DI\Extensions\DIExtension::class, ['%debugMode%']],
44
                'extensions' => Nette\DI\Extensions\ExtensionsExtension::class,
45
                'forms' => Nette\Bridges\FormsDI\FormsExtension::class,
46
                'http' => [Nette\Bridges\HttpDI\HttpExtension::class, ['%consoleMode%']],
47
                'inject' => Nette\DI\Extensions\InjectExtension::class,
48
                'latte' => [Nette\Bridges\ApplicationDI\LatteExtension::class, ['%tempDir%/cache/latte', '%debugMode%']],
49
                'mail' => Nette\Bridges\MailDI\MailExtension::class,
50
                'php' => Extensions\PhpExtension::class,
51
                'routing' => [Nette\Bridges\ApplicationDI\RoutingExtension::class, ['%debugMode%']],
52
                'search' => [Nette\DI\Extensions\SearchExtension::class, ['%tempDir%/cache/nette.search']],
53
                'security' => [Nette\Bridges\SecurityDI\SecurityExtension::class, ['%debugMode%']],
54
                'session' => [Nette\Bridges\HttpDI\SessionExtension::class, ['%debugMode%', '%consoleMode%']],
55
                'tracy' => [Tracy\Bridges\Nette\TracyExtension::class, ['%debugMode%', '%consoleMode%']],
56
        ];
57

58
        /** @var list<class-string>  classes which shouldn't be autowired */
59
        public array $autowireExcludedClasses = [
60
                \ArrayAccess::class,
61
                \Countable::class,
62
                \IteratorAggregate::class,
63
                \stdClass::class,
64
                \Traversable::class,
65
        ];
66

67
        /** @var array<string, mixed> */
68
        protected array $staticParameters;
69

70
        /** @var array<string, mixed> */
71
        protected array $dynamicParameters = [];
72

73
        /** @var array<string, object> */
74
        protected array $services = [];
75

76
        /** @var list<string|array<string, mixed>> */
77
        protected array $configs = [];
78

79
        /** @var array<string, mixed> */
80
        private array $defaultParameters;
81

82

83
        public function __construct()
84
        {
85
                $this->defaultParameters = $this->staticParameters = $this->getDefaultParameters();
1✔
86

87
                if (class_exists(InstalledVersions::class) // back compatibility
1✔
88
                        && InstalledVersions::isInstalled('nette/caching')
1✔
89
                        && version_compare(InstalledVersions::getVersion('nette/caching'), '3.3.0', '<')
1✔
90
                ) {
91
                        $this->defaultExtensions['cache'][1][0] = '%tempDir%';
×
92
                }
93
        }
1✔
94

95

96
        /**
97
         * Sets the %debugMode% parameter.
98
         * @param  bool|string|list<string>  $value  IP addresses or computer names whitelist, or true/false
99
         */
100
        public function setDebugMode(bool|string|array $value): static
1✔
101
        {
102
                if (is_string($value) || is_array($value)) {
1✔
103
                        $value = static::detectDebugMode($value);
1✔
104
                }
105

106
                return $this->addStaticParameters([
1✔
107
                        'debugMode' => $value,
1✔
108
                        'productionMode' => !$value, // compatibility
1✔
109
                ]);
110
        }
111

112

113
        public function isDebugMode(): bool
114
        {
115
                return $this->staticParameters['debugMode'];
1✔
116
        }
117

118

119
        /**
120
         * Sets path to temporary directory.
121
         */
122
        public function setTempDirectory(string $path): static
1✔
123
        {
124
                return $this->addStaticParameters(['tempDir' => $path]);
1✔
125
        }
126

127

128
        /**
129
         * Sets the default timezone.
130
         */
131
        public function setTimeZone(string $timezone): static
1✔
132
        {
133
                date_default_timezone_set($timezone);
1✔
134
                @ini_set('date.timezone', $timezone); // @ - function may be disabled
1✔
135
                return $this;
1✔
136
        }
137

138

139
        /**
140
         * @deprecated use addStaticParameters()
141
         * @param  array<string, mixed>  $params
142
         */
143
        public function addParameters(array $params): static
1✔
144
        {
145
                return $this->addStaticParameters($params);
1✔
146
        }
147

148

149
        /**
150
         * Adds static parameters.
151
         * @param  array<string, mixed>  $params
152
         */
153
        public function addStaticParameters(array $params): static
1✔
154
        {
155
                $this->staticParameters = DI\Config\Helpers::merge($params, $this->staticParameters);
1✔
156
                $this->defaultParameters = array_diff_key($this->defaultParameters, $params);
1✔
157
                return $this;
1✔
158
        }
159

160

161
        /**
162
         * Adds dynamic parameters.
163
         * @param  array<string, mixed>  $params
164
         */
165
        public function addDynamicParameters(array $params): static
1✔
166
        {
167
                $this->dynamicParameters = $params + $this->dynamicParameters;
1✔
168
                return $this;
1✔
169
        }
170

171

172
        /**
173
         * Adds service instances.
174
         * @param  array<string, object>  $services
175
         */
176
        public function addServices(array $services): static
1✔
177
        {
178
                $this->services = $services + $this->services;
1✔
179
                return $this;
1✔
180
        }
181

182

183
        /** @return array<string, mixed> */
184
        protected function getDefaultParameters(): array
185
        {
186
                $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
1✔
187
                $last = end($trace);
1✔
188
                $debugMode = static::detectDebugMode();
1✔
189
                $loaderRc = class_exists(ClassLoader::class)
1✔
190
                        ? new \ReflectionClass(ClassLoader::class)
1✔
191
                        : null;
×
192
                $rootDir = class_exists(InstalledVersions::class)
1✔
193
                        ? rtrim(Nette\Utils\FileSystem::normalizePath(InstalledVersions::getRootPackage()['install_path']), '\/')
1✔
194
                        : null;
×
195
                $baseUrl = new Statement('trim($this->getByType(?)->getUrl()->getBaseUrl(), "/")', [Nette\Http\IRequest::class]);
1✔
196
                return [
197
                        'appDir' => isset($trace[1]['file']) ? dirname($trace[1]['file']) : null,
1✔
198
                        'wwwDir' => isset($last['file']) ? dirname($last['file']) : null,
1✔
199
                        'vendorDir' => $loaderRc ? dirname($loaderRc->getFileName(), 2) : null,
1✔
200
                        'rootDir' => $rootDir,
1✔
201
                        'debugMode' => $debugMode,
1✔
202
                        'productionMode' => !$debugMode,
1✔
203
                        'consoleMode' => PHP_SAPI === 'cli',
204
                        'baseUrl' => $baseUrl,
1✔
205
                ];
206
        }
207

208

209
        /**
210
         * Enables Tracy debugger and configures it for the current mode.
211
         */
212
        public function enableTracy(?string $logDirectory = null, ?string $email = null): void
213
        {
214
                if (!class_exists(Tracy\Debugger::class)) {
×
215
                        throw new Nette\NotSupportedException('Tracy not found, do you have `tracy/tracy` package installed?');
×
216
                }
217

218
                Tracy\Debugger::$strictMode = true;
×
219
                Tracy\Debugger::enable(!$this->staticParameters['debugMode'], $logDirectory, $email);
×
220
                Tracy\Bridges\Nette\Bridge::initialize();
×
221
                if (class_exists(Latte\Bridges\Tracy\BlueScreenPanel::class)) {
×
222
                        Latte\Bridges\Tracy\BlueScreenPanel::initialize();
×
223
                }
224
        }
225

226

227
        /** @deprecated use enableTracy() */
228
        public function enableDebugger(?string $logDirectory = null, ?string $email = null): void
229
        {
230
                $this->enableTracy($logDirectory, $email);
×
231
        }
232

233

234
        /**
235
         * Creates RobotLoader for automatic class discovery and caching.
236
         * @throws Nette\NotSupportedException if RobotLoader is not available
237
         */
238
        public function createRobotLoader(): Nette\Loaders\RobotLoader
239
        {
240
                if (!class_exists(Nette\Loaders\RobotLoader::class)) {
1✔
241
                        throw new Nette\NotSupportedException('RobotLoader not found, do you have `nette/robot-loader` package installed?');
×
242
                }
243

244
                $loader = new Nette\Loaders\RobotLoader;
1✔
245
                $loader->setTempDirectory($this->getCacheDirectory() . '/nette.robotLoader');
1✔
246
                $loader->setAutoRefresh($this->staticParameters['debugMode']);
1✔
247

248
                if (isset($this->defaultExtensions['application'])) {
1✔
249
                        $this->defaultExtensions['application'][1][1] = null;
1✔
250
                        $this->defaultExtensions['application'][1][3] = $loader;
1✔
251
                }
252

253
                return $loader;
1✔
254
        }
255

256

257
        /**
258
         * Adds a configuration file path or configuration array.
259
         * @param  string|array<string, mixed>  $config
260
         */
261
        public function addConfig(string|array $config): static
1✔
262
        {
263
                $this->configs[] = $config;
1✔
264
                return $this;
1✔
265
        }
266

267

268
        /**
269
         * Returns system DI container.
270
         */
271
        public function createContainer(bool $initialize = true): DI\Container
1✔
272
        {
273
                $class = $this->loadContainer();
1✔
274
                $container = new $class($this->dynamicParameters);
1✔
275
                foreach ($this->services as $name => $service) {
1✔
276
                        $container->addService($name, $service);
1✔
277
                }
278

279
                if ($initialize) {
1✔
280
                        $container->initialize();
1✔
281
                }
282

283
                return $container;
1✔
284
        }
285

286

287
        /**
288
         * Loads system DI container class and returns its name.
289
         * @return class-string<DI\Container>
290
         */
291
        public function loadContainer(): string
292
        {
293
                $loader = new DI\ContainerLoader(
1✔
294
                        $this->getCacheDirectory() . '/nette.configurator',
1✔
295
                        $this->staticParameters['debugMode'],
1✔
296
                );
297
                return $loader->load(
1✔
298
                        $this->generateContainer(...),
1✔
299
                        $this->generateContainerKey(),
1✔
300
                );
301
        }
302

303

304
        /**
305
         * @internal
306
         */
307
        public function generateContainer(DI\Compiler $compiler): void
1✔
308
        {
309
                $loader = $this->createLoader();
1✔
310
                $loader->setParameters($this->staticParameters);
1✔
311

312
                $compiler->addConfig(['parameters' => DI\Helpers::escape($this->defaultParameters)]);
1✔
313

314
                foreach ($this->configs as $config) {
1✔
315
                        if (is_string($config)) {
1✔
316
                                $compiler->loadConfig($config, $loader);
1✔
317
                        } else {
318
                                $compiler->addConfig($config);
1✔
319
                        }
320
                }
321

322
                $explicit = array_diff_key($this->staticParameters, $this->defaultParameters);
1✔
323
                $compiler->addConfig(['parameters' => DI\Helpers::escape($explicit)]);
1✔
324
                $compiler->setDynamicParameterNames(array_merge(array_keys($this->dynamicParameters), ['baseUrl']));
1✔
325

326
                $builder = $compiler->getContainerBuilder();
1✔
327
                $builder->addExcludedClasses($this->autowireExcludedClasses);
1✔
328

329
                foreach ($this->defaultExtensions as $name => $extension) {
1✔
330
                        [$class, $args] = is_string($extension)
1✔
331
                                ? [$extension, []]
1✔
332
                                : $extension;
1✔
333
                        if (class_exists($class)) {
1✔
334
                                $args = DI\Helpers::expand($args, $this->staticParameters);
1✔
335
                                $compiler->addExtension($name, (new \ReflectionClass($class))->newInstanceArgs($args));
1✔
336
                        }
337
                }
338

339
                Nette\Utils\Arrays::invoke($this->onCompile, $this, $compiler);
1✔
340
        }
1✔
341

342

343
        protected function createLoader(): DI\Config\Loader
344
        {
345
                return new DI\Config\Loader;
1✔
346
        }
347

348

349
        /** @return list<mixed> */
350
        protected function generateContainerKey(): array
351
        {
352
                return [
353
                        $this->staticParameters,
1✔
354
                        array_diff_key($this->staticParameters, $this->defaultParameters),
1✔
355
                        array_keys($this->dynamicParameters),
1✔
356
                        $this->configs,
1✔
357
                        PHP_VERSION_ID - PHP_RELEASE_VERSION, // minor PHP version
358
                        class_exists(ClassLoader::class) // composer update
1✔
359
                                ? filemtime((new \ReflectionClass(ClassLoader::class))->getFilename())
1✔
360
                                : null,
361
                ];
362
        }
363

364

365
        protected function getCacheDirectory(): string
366
        {
367
                if (empty($this->staticParameters['tempDir'])) {
1✔
368
                        throw new Nette\InvalidStateException('Set path to temporary directory using setTempDirectory().');
1✔
369
                }
370

371
                $dir = $this->staticParameters['tempDir'] . '/cache';
1✔
372
                Nette\Utils\FileSystem::createDir($dir);
1✔
373
                return $dir;
1✔
374
        }
375

376

377
        /**
378
         * Detects debug mode based on IP address or computer name matching.
379
         * @param  string|list<string>|null  $list  IP addresses or computer names whitelist
380
         */
381
        public static function detectDebugMode(string|array|null $list = null): bool
1✔
382
        {
383
                $addr = $_SERVER['REMOTE_ADDR'] ?? php_uname('n');
1✔
384
                $secret = is_string($_COOKIE[self::CookieSecret] ?? null)
1✔
385
                        ? $_COOKIE[self::CookieSecret]
1✔
386
                        : null;
1✔
387
                $list = is_string($list)
1✔
388
                        ? preg_split('#[,\s]+#', $list)
1✔
389
                        : (array) $list;
1✔
390
                if (!isset($_SERVER['HTTP_X_FORWARDED_FOR']) && !isset($_SERVER['HTTP_FORWARDED'])) {
1✔
391
                        $list[] = '127.0.0.1';
1✔
392
                        $list[] = '::1';
1✔
393
                }
394

395
                return in_array($addr, $list, strict: true) || in_array("$secret@$addr", $list, strict: true);
1✔
396
        }
397
}
398

399

400
class_exists(Nette\Configurator::class);
1✔
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