1: <?php
2:
3: 4: 5: 6:
7:
8: namespace Nette;
9:
10: use Nette;
11: use Nette\DI;
12: use Tracy;
13:
14:
15: 16: 17:
18: class Configurator
19: {
20: use SmartObject;
21:
22: const AUTO = true,
23: NONE = false;
24:
25: const COOKIE_SECRET = 'nette-debug';
26:
27:
28: public $onCompile;
29:
30:
31: public $defaultExtensions = [
32: 'php' => Nette\DI\Extensions\PhpExtension::class,
33: 'constants' => Nette\DI\Extensions\ConstantsExtension::class,
34: 'extensions' => Nette\DI\Extensions\ExtensionsExtension::class,
35: 'application' => [Nette\Bridges\ApplicationDI\ApplicationExtension::class, ['%debugMode%', ['%appDir%'], '%tempDir%/cache']],
36: 'decorator' => Nette\DI\Extensions\DecoratorExtension::class,
37: 'cache' => [Nette\Bridges\CacheDI\CacheExtension::class, ['%tempDir%']],
38: 'database' => [Nette\Bridges\DatabaseDI\DatabaseExtension::class, ['%debugMode%']],
39: 'di' => [Nette\DI\Extensions\DIExtension::class, ['%debugMode%']],
40: 'forms' => Nette\Bridges\FormsDI\FormsExtension::class,
41: 'http' => [Nette\Bridges\HttpDI\HttpExtension::class, ['%consoleMode%']],
42: 'latte' => [Nette\Bridges\ApplicationDI\LatteExtension::class, ['%tempDir%/cache/latte', '%debugMode%']],
43: 'mail' => Nette\Bridges\MailDI\MailExtension::class,
44: 'routing' => [Nette\Bridges\ApplicationDI\RoutingExtension::class, ['%debugMode%']],
45: 'security' => [Nette\Bridges\SecurityDI\SecurityExtension::class, ['%debugMode%']],
46: 'session' => [Nette\Bridges\HttpDI\SessionExtension::class, ['%debugMode%', '%consoleMode%']],
47: 'tracy' => [Tracy\Bridges\Nette\TracyExtension::class, ['%debugMode%', '%consoleMode%']],
48: 'inject' => Nette\DI\Extensions\InjectExtension::class,
49: ];
50:
51:
52: public $autowireExcludedClasses = [
53: 'stdClass',
54: ];
55:
56:
57: protected $parameters;
58:
59:
60: protected $dynamicParameters = [];
61:
62:
63: protected $services = [];
64:
65:
66: protected $files = [];
67:
68:
69: public function __construct()
70: {
71: $this->parameters = $this->getDefaultParameters();
72: }
73:
74:
75: 76: 77: 78: 79:
80: public function setDebugMode($value)
81: {
82: if (is_string($value) || is_array($value)) {
83: $value = static::detectDebugMode($value);
84: } elseif (!is_bool($value)) {
85: throw new Nette\InvalidArgumentException(sprintf('Value must be either a string, array, or boolean, %s given.', gettype($value)));
86: }
87: $this->parameters['debugMode'] = $value;
88: $this->parameters['productionMode'] = !$this->parameters['debugMode'];
89: return $this;
90: }
91:
92:
93: 94: 95:
96: public function isDebugMode()
97: {
98: return $this->parameters['debugMode'];
99: }
100:
101:
102: 103: 104: 105: 106:
107: public function setTempDirectory($path)
108: {
109: $this->parameters['tempDir'] = $path;
110: return $this;
111: }
112:
113:
114: 115: 116: 117: 118:
119: public function setTimeZone($timezone)
120: {
121: date_default_timezone_set($timezone);
122: @ini_set('date.timezone', $timezone);
123: return $this;
124: }
125:
126:
127: 128: 129: 130:
131: public function addParameters(array $params)
132: {
133: $this->parameters = DI\Config\Helpers::merge($params, $this->parameters);
134: return $this;
135: }
136:
137:
138: 139: 140: 141:
142: public function addDynamicParameters(array $params)
143: {
144: $this->dynamicParameters = $params + $this->dynamicParameters;
145: return $this;
146: }
147:
148:
149: 150: 151: 152:
153: public function addServices(array $services)
154: {
155: $this->services = $services + $this->services;
156: return $this;
157: }
158:
159:
160: 161: 162:
163: protected function getDefaultParameters()
164: {
165: $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
166: $last = end($trace);
167: $debugMode = static::detectDebugMode();
168: return [
169: 'appDir' => isset($trace[1]['file']) ? dirname($trace[1]['file']) : null,
170: 'wwwDir' => isset($last['file']) ? dirname($last['file']) : null,
171: 'debugMode' => $debugMode,
172: 'productionMode' => !$debugMode,
173: 'consoleMode' => PHP_SAPI === 'cli',
174: ];
175: }
176:
177:
178: 179: 180: 181: 182:
183: public function enableTracy($logDirectory = null, $email = null)
184: {
185: $this->enableDebugger($logDirectory, $email);
186: }
187:
188:
189: 190: 191: 192: 193: 194:
195: public function enableDebugger($logDirectory = null, $email = null)
196: {
197: Tracy\Debugger::$strictMode = true;
198: Tracy\Debugger::enable(!$this->parameters['debugMode'], $logDirectory, $email);
199: Nette\Bridges\Framework\TracyBridge::initialize();
200: }
201:
202:
203: 204: 205: 206:
207: public function createRobotLoader()
208: {
209: if (!class_exists(Nette\Loaders\RobotLoader::class)) {
210: throw new Nette\NotSupportedException('RobotLoader not found, do you have `nette/robot-loader` package installed?');
211: }
212:
213: $loader = new Nette\Loaders\RobotLoader;
214: $loader->setTempDirectory($this->getCacheDirectory() . '/Nette.RobotLoader');
215: $loader->setAutoRefresh($this->parameters['debugMode']);
216: return $loader;
217: }
218:
219:
220: 221: 222: 223: 224:
225: public function addConfig($file)
226: {
227: $section = func_num_args() > 1 ? func_get_arg(1) : null;
228: if ($section !== null) {
229: trigger_error('Sections in config file are deprecated.', E_USER_DEPRECATED);
230: }
231: $this->files[] = [$file, $section === self::AUTO ? ($this->parameters['debugMode'] ? 'development' : 'production') : $section];
232: return $this;
233: }
234:
235:
236: 237: 238: 239:
240: public function createContainer()
241: {
242: $class = $this->loadContainer();
243: $container = new $class($this->dynamicParameters);
244: foreach ($this->services as $name => $service) {
245: $container->addService($name, $service);
246: }
247: $container->initialize();
248: if (class_exists(Nette\Environment::class)) {
249: Nette\Environment::setContext($container);
250: }
251: return $container;
252: }
253:
254:
255: 256: 257: 258:
259: public function loadContainer()
260: {
261: $loader = new DI\ContainerLoader(
262: $this->getCacheDirectory() . '/Nette.Configurator',
263: $this->parameters['debugMode']
264: );
265: $class = $loader->load(
266: [$this, 'generateContainer'],
267: [$this->parameters, array_keys($this->dynamicParameters), $this->files, PHP_VERSION_ID - PHP_RELEASE_VERSION]
268: );
269: return $class;
270: }
271:
272:
273: 274: 275: 276:
277: public function generateContainer(DI\Compiler $compiler)
278: {
279: $compiler->addConfig(['parameters' => $this->parameters]);
280: $compiler->setDynamicParameterNames(array_keys($this->dynamicParameters));
281:
282: $loader = $this->createLoader();
283: $fileInfo = [];
284: foreach ($this->files as $info) {
285: if (is_scalar($info[0])) {
286: $fileInfo[] = "// source: $info[0] $info[1]";
287: $info[0] = $loader->load($info[0], $info[1]);
288: }
289: $compiler->addConfig($this->fixCompatibility($info[0]));
290: }
291: $compiler->addDependencies($loader->getDependencies());
292:
293: $builder = $compiler->getContainerBuilder();
294: $builder->addExcludedClasses($this->autowireExcludedClasses);
295:
296: foreach ($this->defaultExtensions as $name => $extension) {
297: list($class, $args) = is_string($extension) ? [$extension, []] : $extension;
298: if (class_exists($class)) {
299: $args = DI\Helpers::expand($args, $this->parameters, true);
300: $compiler->addExtension($name, (new \ReflectionClass($class))->newInstanceArgs($args));
301: }
302: }
303:
304: $this->onCompile($this, $compiler);
305:
306: $classes = $compiler->compile();
307: return implode("\n", $fileInfo) . "\n\n" . $classes;
308: }
309:
310:
311: 312: 313:
314: protected function createLoader()
315: {
316: return new DI\Config\Loader;
317: }
318:
319:
320: 321: 322:
323: protected function getCacheDirectory()
324: {
325: if (empty($this->parameters['tempDir'])) {
326: throw new Nette\InvalidStateException('Set path to temporary directory using setTempDirectory().');
327: }
328: $dir = $this->parameters['tempDir'] . '/cache';
329: Nette\Utils\FileSystem::createDir($dir);
330: return $dir;
331: }
332:
333:
334: 335: 336: 337:
338: protected function fixCompatibility($config)
339: {
340: if (isset($config['nette']['security']['frames'])) {
341: $config['nette']['http']['frames'] = $config['nette']['security']['frames'];
342: unset($config['nette']['security']['frames']);
343: }
344: foreach (['application', 'cache', 'database', 'di' => 'container', 'forms', 'http',
345: 'latte', 'mail' => 'mailer', 'routing', 'security', 'session', 'tracy' => 'debugger', ] as $new => $old) {
346: if (isset($config['nette'][$old])) {
347: $new = is_int($new) ? $old : $new;
348: if (isset($config[$new])) {
349: throw new Nette\DeprecatedException("You can use (deprecated) section 'nette.$old' or new section '$new', but not both of them.");
350: } else {
351: trigger_error("Configuration section 'nette.$old' is deprecated, use section '$new' (without 'nette')", E_USER_DEPRECATED);
352: }
353: $config[$new] = $config['nette'][$old];
354: unset($config['nette'][$old]);
355: }
356: }
357: if (isset($config['nette']['xhtml'])) {
358: trigger_error("Configuration option 'nette.xhtml' is deprecated, use section 'latte.xhtml' instead.", E_USER_DEPRECATED);
359: $config['latte']['xhtml'] = $config['nette']['xhtml'];
360: unset($config['nette']['xhtml']);
361: }
362:
363: if (empty($config['nette'])) {
364: unset($config['nette']);
365: }
366: return $config;
367: }
368:
369:
370:
371:
372:
373: 374: 375: 376: 377:
378: public static function detectDebugMode($list = null)
379: {
380: $addr = isset($_SERVER['REMOTE_ADDR'])
381: ? $_SERVER['REMOTE_ADDR']
382: : php_uname('n');
383: $secret = isset($_COOKIE[self::COOKIE_SECRET]) && is_string($_COOKIE[self::COOKIE_SECRET])
384: ? $_COOKIE[self::COOKIE_SECRET]
385: : null;
386: $list = is_string($list)
387: ? preg_split('#[,\s]+#', $list)
388: : (array) $list;
389: if (!isset($_SERVER['HTTP_X_FORWARDED_FOR']) && !isset($_SERVER['HTTP_FORWARDED'])) {
390: $list[] = '127.0.0.1';
391: $list[] = '::1';
392: }
393: return in_array($addr, $list, true) || in_array("$secret@$addr", $list, true);
394: }
395: }
396: