├── composer.json ├── config.stub.neon ├── license.md ├── readme.md └── src ├── Bootstrap ├── Configurator.php └── Extensions │ ├── ConstantsExtension.php │ └── PhpExtension.php └── Configurator.php /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nette/bootstrap", 3 | "description": "🅱 Nette Bootstrap: the simple way to configure and bootstrap your Nette application.", 4 | "keywords": ["nette", "configurator", "bootstrapping"], 5 | "homepage": "https://nette.org", 6 | "license": ["BSD-3-Clause", "GPL-2.0-only", "GPL-3.0-only"], 7 | "authors": [ 8 | { 9 | "name": "David Grudl", 10 | "homepage": "https://davidgrudl.com" 11 | }, 12 | { 13 | "name": "Nette Community", 14 | "homepage": "https://nette.org/contributors" 15 | } 16 | ], 17 | "require": { 18 | "php": "8.0 - 8.4", 19 | "nette/di": "^3.1", 20 | "nette/utils": "^3.2.1 || ^4.0" 21 | }, 22 | "suggest": { 23 | "nette/robot-loader": "to use Configurator::createRobotLoader()", 24 | "tracy/tracy": "to use Configurator::enableTracy()" 25 | }, 26 | "require-dev": { 27 | "nette/application": "^3.1", 28 | "nette/caching": "^3.0", 29 | "nette/database": "^3.0", 30 | "nette/forms": "^3.0", 31 | "nette/http": "^3.0", 32 | "nette/mail": "^3.0 || ^4.0", 33 | "nette/robot-loader": "^3.0 || ^4.0", 34 | "nette/safe-stream": "^2.2", 35 | "nette/security": "^3.0", 36 | "nette/tester": "^2.4", 37 | "latte/latte": "^2.8 || ^3.0", 38 | "tracy/tracy": "^2.9", 39 | "phpstan/phpstan-nette": "^2.0@stable" 40 | }, 41 | "conflict": { 42 | "tracy/tracy": "<2.6" 43 | }, 44 | "autoload": { 45 | "classmap": ["src/"], 46 | "psr-4": { 47 | "Nette\\": "src" 48 | } 49 | }, 50 | "minimum-stability": "dev", 51 | "scripts": { 52 | "phpstan": "phpstan analyse", 53 | "tester": "tester tests -s" 54 | }, 55 | "extra": { 56 | "branch-alias": { 57 | "dev-master": "3.2-dev" 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /config.stub.neon: -------------------------------------------------------------------------------- 1 | # Default parameters created by Nette\Bootstrap\Configurator 2 | parameters: 3 | appDir: absolute path to the directory with the Bootstrap.php file 4 | wwwDir: absolute path to the directory containing the index.php entry file 5 | tempDir: absolute path to the directory for temporary files 6 | vendorDir: absolute path to the directory where Composer installs libraries 7 | rootDir: absolute path to the root project directory 8 | baseUrl: absolute URL of the root project directory 9 | debugMode: indicates whether the application is in debug mode 10 | productionMode: opposite of debugMode 11 | consoleMode: indicates whether the request came through the command line (CLI) 12 | -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | Licenses 2 | ======== 3 | 4 | Good news! You may use Nette Framework under the terms of either 5 | the New BSD License or the GNU General Public License (GPL) version 2 or 3. 6 | 7 | The BSD License is recommended for most projects. It is easy to understand and it 8 | places almost no restrictions on what you can do with the framework. If the GPL 9 | fits better to your project, you can use the framework under this license. 10 | 11 | You don't have to notify anyone which license you are using. You can freely 12 | use Nette Framework in commercial projects as long as the copyright header 13 | remains intact. 14 | 15 | Please be advised that the name "Nette Framework" is a protected trademark and its 16 | usage has some limitations. So please do not use word "Nette" in the name of your 17 | project or top-level domain, and choose a name that stands on its own merits. 18 | If your stuff is good, it will not take long to establish a reputation for yourselves. 19 | 20 | 21 | New BSD License 22 | --------------- 23 | 24 | Copyright (c) 2004, 2014 David Grudl (https://davidgrudl.com) 25 | All rights reserved. 26 | 27 | Redistribution and use in source and binary forms, with or without modification, 28 | are permitted provided that the following conditions are met: 29 | 30 | * Redistributions of source code must retain the above copyright notice, 31 | this list of conditions and the following disclaimer. 32 | 33 | * Redistributions in binary form must reproduce the above copyright notice, 34 | this list of conditions and the following disclaimer in the documentation 35 | and/or other materials provided with the distribution. 36 | 37 | * Neither the name of "Nette Framework" nor the names of its contributors 38 | may be used to endorse or promote products derived from this software 39 | without specific prior written permission. 40 | 41 | This software is provided by the copyright holders and contributors "as is" and 42 | any express or implied warranties, including, but not limited to, the implied 43 | warranties of merchantability and fitness for a particular purpose are 44 | disclaimed. In no event shall the copyright owner or contributors be liable for 45 | any direct, indirect, incidental, special, exemplary, or consequential damages 46 | (including, but not limited to, procurement of substitute goods or services; 47 | loss of use, data, or profits; or business interruption) however caused and on 48 | any theory of liability, whether in contract, strict liability, or tort 49 | (including negligence or otherwise) arising in any way out of the use of this 50 | software, even if advised of the possibility of such damage. 51 | 52 | 53 | GNU General Public License 54 | -------------------------- 55 | 56 | GPL licenses are very very long, so instead of including them here we offer 57 | you URLs with full text: 58 | 59 | - [GPL version 2](http://www.gnu.org/licenses/gpl-2.0.html) 60 | - [GPL version 3](http://www.gnu.org/licenses/gpl-3.0.html) 61 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | Nette Bootstrap 2 | =============== 3 | 4 | [![Downloads this Month](https://img.shields.io/packagist/dm/nette/bootstrap.svg)](https://packagist.org/packages/nette/bootstrap) 5 | [![Tests](https://github.com/nette/bootstrap/workflows/Tests/badge.svg?branch=master)](https://github.com/nette/bootstrap/actions) 6 | [![Coverage Status](https://coveralls.io/repos/github/nette/bootstrap/badge.svg?branch=master)](https://coveralls.io/github/nette/bootstrap?branch=master) 7 | [![Latest Stable Version](https://poser.pugx.org/nette/bootstrap/v/stable)](https://github.com/nette/bootstrap/releases) 8 | [![License](https://img.shields.io/badge/license-New%20BSD-blue.svg)](https://github.com/nette/bootstrap/blob/master/license.md) 9 | 10 | 11 | Introduction 12 | ============ 13 | 14 | Setting environment and creating a Dependency Injection (DI) container is in Nette in charge of the Nette Bootstrap. 15 | 16 | Documentation can be found on the [website](https://doc.nette.org/bootstrap). 17 | 18 | If you like Nette, **[please make a donation now](https://nette.org/donate)**. Thank you! 19 | 20 | 21 | Installation 22 | ============ 23 | 24 | The recommended way to install is via Composer: 25 | 26 | ``` 27 | composer require nette/bootstrap 28 | ``` 29 | 30 | It requires PHP version 8.0 and supports PHP up to 8.4. 31 | -------------------------------------------------------------------------------- /src/Bootstrap/Configurator.php: -------------------------------------------------------------------------------- 1 | Occurs after the compiler is created */ 33 | public array $onCompile = []; 34 | 35 | public array $defaultExtensions = [ 36 | 'application' => [Nette\Bridges\ApplicationDI\ApplicationExtension::class, ['%debugMode%', ['%appDir%'], '%tempDir%/cache/nette.application']], 37 | 'assets' => [Nette\Bridges\AssetsDI\DIExtension::class, ['%baseUrl%', '%wwwDir%', '%debugMode%']], 38 | 'cache' => [Nette\Bridges\CacheDI\CacheExtension::class, ['%tempDir%/cache']], 39 | 'constants' => Extensions\ConstantsExtension::class, 40 | 'database' => [Nette\Bridges\DatabaseDI\DatabaseExtension::class, ['%debugMode%']], 41 | 'decorator' => Nette\DI\Extensions\DecoratorExtension::class, 42 | 'di' => [Nette\DI\Extensions\DIExtension::class, ['%debugMode%']], 43 | 'extensions' => Nette\DI\Extensions\ExtensionsExtension::class, 44 | 'forms' => Nette\Bridges\FormsDI\FormsExtension::class, 45 | 'http' => [Nette\Bridges\HttpDI\HttpExtension::class, ['%consoleMode%']], 46 | 'inject' => Nette\DI\Extensions\InjectExtension::class, 47 | 'latte' => [Nette\Bridges\ApplicationDI\LatteExtension::class, ['%tempDir%/cache/latte', '%debugMode%']], 48 | 'mail' => Nette\Bridges\MailDI\MailExtension::class, 49 | 'php' => Extensions\PhpExtension::class, 50 | 'routing' => [Nette\Bridges\ApplicationDI\RoutingExtension::class, ['%debugMode%']], 51 | 'search' => [Nette\DI\Extensions\SearchExtension::class, ['%tempDir%/cache/nette.search']], 52 | 'security' => [Nette\Bridges\SecurityDI\SecurityExtension::class, ['%debugMode%']], 53 | 'session' => [Nette\Bridges\HttpDI\SessionExtension::class, ['%debugMode%', '%consoleMode%']], 54 | 'tracy' => [Tracy\Bridges\Nette\TracyExtension::class, ['%debugMode%', '%consoleMode%']], 55 | ]; 56 | 57 | /** @var string[] of classes which shouldn't be autowired */ 58 | public array $autowireExcludedClasses = [ 59 | \ArrayAccess::class, 60 | \Countable::class, 61 | \IteratorAggregate::class, 62 | \stdClass::class, 63 | \Traversable::class, 64 | ]; 65 | 66 | protected array $staticParameters; 67 | protected array $dynamicParameters = []; 68 | protected array $services = []; 69 | 70 | /** @var array */ 71 | protected array $configs = []; 72 | 73 | 74 | public function __construct() 75 | { 76 | $this->staticParameters = $this->getDefaultParameters(); 77 | 78 | if (class_exists(InstalledVersions::class) // back compatibility 79 | && InstalledVersions::isInstalled('nette/caching') 80 | && version_compare(InstalledVersions::getVersion('nette/caching'), '3.3.0', '<') 81 | ) { 82 | $this->defaultExtensions['cache'][1][0] = '%tempDir%'; 83 | } 84 | } 85 | 86 | 87 | /** 88 | * Set parameter %debugMode%. 89 | */ 90 | public function setDebugMode(bool|string|array $value): static 91 | { 92 | if (is_string($value) || is_array($value)) { 93 | $value = static::detectDebugMode($value); 94 | } 95 | 96 | $this->staticParameters['debugMode'] = $value; 97 | $this->staticParameters['productionMode'] = !$this->staticParameters['debugMode']; // compatibility 98 | return $this; 99 | } 100 | 101 | 102 | public function isDebugMode(): bool 103 | { 104 | return $this->staticParameters['debugMode']; 105 | } 106 | 107 | 108 | /** 109 | * Sets path to temporary directory. 110 | */ 111 | public function setTempDirectory(string $path): static 112 | { 113 | $this->staticParameters['tempDir'] = $path; 114 | return $this; 115 | } 116 | 117 | 118 | /** 119 | * Sets the default timezone. 120 | */ 121 | public function setTimeZone(string $timezone): static 122 | { 123 | date_default_timezone_set($timezone); 124 | @ini_set('date.timezone', $timezone); // @ - function may be disabled 125 | return $this; 126 | } 127 | 128 | 129 | /** @deprecated use addStaticParameters() */ 130 | public function addParameters(array $params): static 131 | { 132 | return $this->addStaticParameters($params); 133 | } 134 | 135 | 136 | /** 137 | * Adds new static parameters. 138 | */ 139 | public function addStaticParameters(array $params): static 140 | { 141 | $this->staticParameters = DI\Config\Helpers::merge($params, $this->staticParameters); 142 | return $this; 143 | } 144 | 145 | 146 | /** 147 | * Adds new dynamic parameters. 148 | */ 149 | public function addDynamicParameters(array $params): static 150 | { 151 | $this->dynamicParameters = $params + $this->dynamicParameters; 152 | return $this; 153 | } 154 | 155 | 156 | /** 157 | * Add instances of services. 158 | */ 159 | public function addServices(array $services): static 160 | { 161 | $this->services = $services + $this->services; 162 | return $this; 163 | } 164 | 165 | 166 | protected function getDefaultParameters(): array 167 | { 168 | $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); 169 | $last = end($trace); 170 | $debugMode = static::detectDebugMode(); 171 | $loaderRc = class_exists(ClassLoader::class) 172 | ? new \ReflectionClass(ClassLoader::class) 173 | : null; 174 | $rootDir = class_exists(InstalledVersions::class) && ($tmp = InstalledVersions::getRootPackage()['install_path'] ?? null) 175 | ? rtrim(Nette\Utils\FileSystem::normalizePath($tmp), '\/') 176 | : null; 177 | $baseUrl = class_exists(Nette\Http\Request::class) 178 | ? new Statement(['', 'rtrim'], [new Statement([new Statement('@Nette\Http\IRequest::getUrl'), 'getBaseUrl']), '/']) 179 | : null; 180 | return [ 181 | 'appDir' => isset($trace[1]['file']) ? dirname($trace[1]['file']) : null, 182 | 'wwwDir' => isset($last['file']) ? dirname($last['file']) : null, 183 | 'vendorDir' => $loaderRc ? dirname($loaderRc->getFileName(), 2) : null, 184 | 'rootDir' => $rootDir, 185 | 'debugMode' => $debugMode, 186 | 'productionMode' => !$debugMode, 187 | 'consoleMode' => PHP_SAPI === 'cli', 188 | 'baseUrl' => $baseUrl, 189 | ]; 190 | } 191 | 192 | 193 | public function enableTracy(?string $logDirectory = null, ?string $email = null): void 194 | { 195 | if (!class_exists(Tracy\Debugger::class)) { 196 | throw new Nette\NotSupportedException('Tracy not found, do you have `tracy/tracy` package installed?'); 197 | } 198 | 199 | Tracy\Debugger::$strictMode = true; 200 | Tracy\Debugger::enable(!$this->staticParameters['debugMode'], $logDirectory, $email); 201 | Tracy\Bridges\Nette\Bridge::initialize(); 202 | if (class_exists(Latte\Bridges\Tracy\BlueScreenPanel::class)) { 203 | Latte\Bridges\Tracy\BlueScreenPanel::initialize(); 204 | } 205 | } 206 | 207 | 208 | /** @deprecated use enableTracy() */ 209 | public function enableDebugger(?string $logDirectory = null, ?string $email = null): void 210 | { 211 | $this->enableTracy($logDirectory, $email); 212 | } 213 | 214 | 215 | /** 216 | * @throws Nette\NotSupportedException if RobotLoader is not available 217 | */ 218 | public function createRobotLoader(): Nette\Loaders\RobotLoader 219 | { 220 | if (!class_exists(Nette\Loaders\RobotLoader::class)) { 221 | throw new Nette\NotSupportedException('RobotLoader not found, do you have `nette/robot-loader` package installed?'); 222 | } 223 | 224 | $loader = new Nette\Loaders\RobotLoader; 225 | $loader->setTempDirectory($this->getCacheDirectory() . '/nette.robotLoader'); 226 | $loader->setAutoRefresh($this->staticParameters['debugMode']); 227 | 228 | if (isset($this->defaultExtensions['application'])) { 229 | $this->defaultExtensions['application'][1][1] = null; 230 | $this->defaultExtensions['application'][1][3] = $loader; 231 | } 232 | 233 | return $loader; 234 | } 235 | 236 | 237 | /** 238 | * Adds configuration file. 239 | */ 240 | public function addConfig(string|array $config): static 241 | { 242 | $this->configs[] = $config; 243 | return $this; 244 | } 245 | 246 | 247 | /** 248 | * Returns system DI container. 249 | */ 250 | public function createContainer(bool $initialize = true): DI\Container 251 | { 252 | $class = $this->loadContainer(); 253 | $container = new $class($this->dynamicParameters); 254 | foreach ($this->services as $name => $service) { 255 | $container->addService($name, $service); 256 | } 257 | 258 | if ($initialize) { 259 | $container->initialize(); 260 | } 261 | 262 | return $container; 263 | } 264 | 265 | 266 | /** 267 | * Loads system DI container class and returns its name. 268 | */ 269 | public function loadContainer(): string 270 | { 271 | $loader = new DI\ContainerLoader( 272 | $this->getCacheDirectory() . '/nette.configurator', 273 | $this->staticParameters['debugMode'], 274 | ); 275 | return $loader->load( 276 | [$this, 'generateContainer'], 277 | $this->generateContainerKey(), 278 | ); 279 | } 280 | 281 | 282 | /** 283 | * @internal 284 | */ 285 | public function generateContainer(DI\Compiler $compiler): void 286 | { 287 | $loader = $this->createLoader(); 288 | $loader->setParameters($this->staticParameters); 289 | 290 | foreach ($this->configs as $config) { 291 | if (is_string($config)) { 292 | $compiler->loadConfig($config, $loader); 293 | } else { 294 | $compiler->addConfig($config); 295 | } 296 | } 297 | 298 | $compiler->addConfig(['parameters' => DI\Helpers::escape($this->staticParameters)]); 299 | $compiler->setDynamicParameterNames(array_merge(array_keys($this->dynamicParameters), ['baseUrl'])); 300 | 301 | $builder = $compiler->getContainerBuilder(); 302 | $builder->addExcludedClasses($this->autowireExcludedClasses); 303 | 304 | foreach ($this->defaultExtensions as $name => $extension) { 305 | [$class, $args] = is_string($extension) 306 | ? [$extension, []] 307 | : $extension; 308 | if (class_exists($class)) { 309 | $args = DI\Helpers::expand($args, $this->staticParameters); 310 | $compiler->addExtension($name, (new \ReflectionClass($class))->newInstanceArgs($args)); 311 | } 312 | } 313 | 314 | Nette\Utils\Arrays::invoke($this->onCompile, $this, $compiler); 315 | } 316 | 317 | 318 | protected function createLoader(): DI\Config\Loader 319 | { 320 | return new DI\Config\Loader; 321 | } 322 | 323 | 324 | protected function generateContainerKey(): array 325 | { 326 | return [ 327 | $this->staticParameters, 328 | array_keys($this->dynamicParameters), 329 | $this->configs, 330 | PHP_VERSION_ID - PHP_RELEASE_VERSION, // minor PHP version 331 | class_exists(ClassLoader::class) // composer update 332 | ? filemtime((new \ReflectionClass(ClassLoader::class))->getFilename()) 333 | : null, 334 | ]; 335 | } 336 | 337 | 338 | protected function getCacheDirectory(): string 339 | { 340 | if (empty($this->staticParameters['tempDir'])) { 341 | throw new Nette\InvalidStateException('Set path to temporary directory using setTempDirectory().'); 342 | } 343 | 344 | $dir = $this->staticParameters['tempDir'] . '/cache'; 345 | Nette\Utils\FileSystem::createDir($dir); 346 | return $dir; 347 | } 348 | 349 | 350 | /** 351 | * Detects debug mode by IP addresses or computer names whitelist detection. 352 | */ 353 | public static function detectDebugMode(string|array|null $list = null): bool 354 | { 355 | $addr = $_SERVER['REMOTE_ADDR'] ?? php_uname('n'); 356 | $secret = is_string($_COOKIE[self::CookieSecret] ?? null) 357 | ? $_COOKIE[self::CookieSecret] 358 | : null; 359 | $list = is_string($list) 360 | ? preg_split('#[,\s]+#', $list) 361 | : (array) $list; 362 | if (!isset($_SERVER['HTTP_X_FORWARDED_FOR']) && !isset($_SERVER['HTTP_FORWARDED'])) { 363 | $list[] = '127.0.0.1'; 364 | $list[] = '::1'; 365 | } 366 | 367 | return in_array($addr, $list, strict: true) || in_array("$secret@$addr", $list, strict: true); 368 | } 369 | } 370 | 371 | 372 | class_exists(Nette\Configurator::class); 373 | -------------------------------------------------------------------------------- /src/Bootstrap/Extensions/ConstantsExtension.php: -------------------------------------------------------------------------------- 1 | dynamic(), 25 | Expect::string(), 26 | ); 27 | } 28 | 29 | 30 | public function loadConfiguration(): void 31 | { 32 | foreach ($this->getConfig() as $name => $value) { 33 | $this->initialization->addBody('define(?, ?);', [$name, $value]); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Bootstrap/Extensions/PhpExtension.php: -------------------------------------------------------------------------------- 1 | dynamic(), 25 | Expect::string(), 26 | ); 27 | } 28 | 29 | 30 | public function loadConfiguration(): void 31 | { 32 | foreach ($this->getConfig() as $name => $value) { 33 | if (!function_exists('ini_set')) { 34 | throw new Nette\NotSupportedException('Required function ini_set() is disabled.'); 35 | } 36 | 37 | if ($value !== null) { 38 | $this->initialization->addBody('ini_set(?, (string) (?));', [$name, $value]); 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Configurator.php: -------------------------------------------------------------------------------- 1 |