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

nette / bootstrap / 20959461507

13 Jan 2026 01:59PM UTC coverage: 89.655%. Remained the same
20959461507

push

github

dg
improved phpDoc

130 of 145 relevant lines covered (89.66%)

0.9 hits per line

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

89.68
/src/Bootstrap/Configurator.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\Bootstrap;
11

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

22

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

30
        /** @deprecated  use Configurator::CookieSecret */
31
        public const COOKIE_SECRET = self::CookieSecret;
32

33

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

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

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

69
        /** @var array<string, mixed> */
70
        protected array $staticParameters;
71

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

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

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

81

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

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

94

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

105
                $this->staticParameters['debugMode'] = $value;
1✔
106
                $this->staticParameters['productionMode'] = !$this->staticParameters['debugMode']; // compatibility
1✔
107
                return $this;
1✔
108
        }
109

110

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

116

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

126

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

137

138
        /** @deprecated use addStaticParameters() */
139
        public function addParameters(array $params): static
1✔
140
        {
141
                return $this->addStaticParameters($params);
1✔
142
        }
143

144

145
        /**
146
         * Adds new static parameters.
147
         * @param  array<string, mixed>  $params
148
         */
149
        public function addStaticParameters(array $params): static
1✔
150
        {
151
                $this->staticParameters = DI\Config\Helpers::merge($params, $this->staticParameters);
1✔
152
                return $this;
1✔
153
        }
154

155

156
        /**
157
         * Adds new dynamic parameters.
158
         * @param  array<string, mixed>  $params
159
         */
160
        public function addDynamicParameters(array $params): static
1✔
161
        {
162
                $this->dynamicParameters = $params + $this->dynamicParameters;
1✔
163
                return $this;
1✔
164
        }
165

166

167
        /**
168
         * Adds instances of services.
169
         * @param  array<string, object>  $services
170
         */
171
        public function addServices(array $services): static
1✔
172
        {
173
                $this->services = $services + $this->services;
1✔
174
                return $this;
1✔
175
        }
176

177

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

202

203
        public function enableTracy(?string $logDirectory = null, ?string $email = null): void
204
        {
205
                if (!class_exists(Tracy\Debugger::class)) {
×
206
                        throw new Nette\NotSupportedException('Tracy not found, do you have `tracy/tracy` package installed?');
×
207
                }
208

209
                Tracy\Debugger::$strictMode = true;
×
210
                Tracy\Debugger::enable(!$this->staticParameters['debugMode'], $logDirectory, $email);
×
211
                Tracy\Bridges\Nette\Bridge::initialize();
×
212
                if (class_exists(Latte\Bridges\Tracy\BlueScreenPanel::class)) {
×
213
                        Latte\Bridges\Tracy\BlueScreenPanel::initialize();
×
214
                }
215
        }
216

217

218
        /** @deprecated use enableTracy() */
219
        public function enableDebugger(?string $logDirectory = null, ?string $email = null): void
220
        {
221
                $this->enableTracy($logDirectory, $email);
×
222
        }
223

224

225
        /**
226
         * @throws Nette\NotSupportedException if RobotLoader is not available
227
         */
228
        public function createRobotLoader(): Nette\Loaders\RobotLoader
229
        {
230
                if (!class_exists(Nette\Loaders\RobotLoader::class)) {
1✔
231
                        throw new Nette\NotSupportedException('RobotLoader not found, do you have `nette/robot-loader` package installed?');
×
232
                }
233

234
                $loader = new Nette\Loaders\RobotLoader;
1✔
235
                $loader->setTempDirectory($this->getCacheDirectory() . '/nette.robotLoader');
1✔
236
                $loader->setAutoRefresh($this->staticParameters['debugMode']);
1✔
237

238
                if (isset($this->defaultExtensions['application'])) {
1✔
239
                        $this->defaultExtensions['application'][1][1] = null;
1✔
240
                        $this->defaultExtensions['application'][1][3] = $loader;
1✔
241
                }
242

243
                return $loader;
1✔
244
        }
245

246

247
        /**
248
         * Adds configuration file.
249
         * @param  string|array<string, mixed>  $config  file path or configuration array
250
         */
251
        public function addConfig(string|array $config): static
1✔
252
        {
253
                $this->configs[] = $config;
1✔
254
                return $this;
1✔
255
        }
256

257

258
        /**
259
         * Returns system DI container.
260
         */
261
        public function createContainer(bool $initialize = true): DI\Container
1✔
262
        {
263
                $class = $this->loadContainer();
1✔
264
                $container = new $class($this->dynamicParameters);
1✔
265
                foreach ($this->services as $name => $service) {
1✔
266
                        $container->addService($name, $service);
1✔
267
                }
268

269
                if ($initialize) {
1✔
270
                        $container->initialize();
1✔
271
                }
272

273
                return $container;
1✔
274
        }
275

276

277
        /**
278
         * Loads system DI container class and returns its name.
279
         */
280
        public function loadContainer(): string
281
        {
282
                $loader = new DI\ContainerLoader(
1✔
283
                        $this->getCacheDirectory() . '/nette.configurator',
1✔
284
                        $this->staticParameters['debugMode'],
1✔
285
                );
286
                return $loader->load(
1✔
287
                        [$this, 'generateContainer'],
1✔
288
                        $this->generateContainerKey(),
1✔
289
                );
290
        }
291

292

293
        /**
294
         * @internal
295
         */
296
        public function generateContainer(DI\Compiler $compiler): void
1✔
297
        {
298
                $loader = $this->createLoader();
1✔
299
                $loader->setParameters($this->staticParameters);
1✔
300

301
                foreach ($this->configs as $config) {
1✔
302
                        if (is_string($config)) {
1✔
303
                                $compiler->loadConfig($config, $loader);
1✔
304
                        } else {
305
                                $compiler->addConfig($config);
×
306
                        }
307
                }
308

309
                $compiler->addConfig(['parameters' => DI\Helpers::escape($this->staticParameters)]);
1✔
310
                $compiler->setDynamicParameterNames(array_merge(array_keys($this->dynamicParameters), ['baseUrl']));
1✔
311

312
                $builder = $compiler->getContainerBuilder();
1✔
313
                $builder->addExcludedClasses($this->autowireExcludedClasses);
1✔
314

315
                foreach ($this->defaultExtensions as $name => $extension) {
1✔
316
                        [$class, $args] = is_string($extension)
1✔
317
                                ? [$extension, []]
1✔
318
                                : $extension;
1✔
319
                        if (class_exists($class)) {
1✔
320
                                $args = DI\Helpers::expand($args, $this->staticParameters);
1✔
321
                                $compiler->addExtension($name, (new \ReflectionClass($class))->newInstanceArgs($args));
1✔
322
                        }
323
                }
324

325
                Nette\Utils\Arrays::invoke($this->onCompile, $this, $compiler);
1✔
326
        }
1✔
327

328

329
        protected function createLoader(): DI\Config\Loader
330
        {
331
                return new DI\Config\Loader;
1✔
332
        }
333

334

335
        protected function generateContainerKey(): array
336
        {
337
                return [
338
                        $this->staticParameters,
1✔
339
                        array_keys($this->dynamicParameters),
1✔
340
                        $this->configs,
1✔
341
                        PHP_VERSION_ID - PHP_RELEASE_VERSION, // minor PHP version
342
                        class_exists(ClassLoader::class) // composer update
1✔
343
                                ? filemtime((new \ReflectionClass(ClassLoader::class))->getFilename())
1✔
344
                                : null,
345
                ];
346
        }
347

348

349
        protected function getCacheDirectory(): string
350
        {
351
                if (empty($this->staticParameters['tempDir'])) {
1✔
352
                        throw new Nette\InvalidStateException('Set path to temporary directory using setTempDirectory().');
1✔
353
                }
354

355
                $dir = $this->staticParameters['tempDir'] . '/cache';
1✔
356
                Nette\Utils\FileSystem::createDir($dir);
1✔
357
                return $dir;
1✔
358
        }
359

360

361
        /**
362
         * Detects debug mode by IP addresses or computer names whitelist detection.
363
         * @param  string|list<string>|null  $list  IP addresses or computer names whitelist
364
         */
365
        public static function detectDebugMode(string|array|null $list = null): bool
1✔
366
        {
367
                $addr = $_SERVER['REMOTE_ADDR'] ?? php_uname('n');
1✔
368
                $secret = is_string($_COOKIE[self::CookieSecret] ?? null)
1✔
369
                        ? $_COOKIE[self::CookieSecret]
1✔
370
                        : null;
1✔
371
                $list = is_string($list)
1✔
372
                        ? preg_split('#[,\s]+#', $list)
1✔
373
                        : (array) $list;
1✔
374
                if (!isset($_SERVER['HTTP_X_FORWARDED_FOR']) && !isset($_SERVER['HTTP_FORWARDED'])) {
1✔
375
                        $list[] = '127.0.0.1';
1✔
376
                        $list[] = '::1';
1✔
377
                }
378

379
                return in_array($addr, $list, strict: true) || in_array("$secret@$addr", $list, strict: true);
1✔
380
        }
381
}
382

383

384
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