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 $services = [];
61:
62:
63: protected $files = [];
64:
65:
66: public function __construct()
67: {
68: $this->parameters = $this->getDefaultParameters();
69: }
70:
71:
72: 73: 74: 75: 76:
77: public function setDebugMode($value)
78: {
79: if (is_string($value) || is_array($value)) {
80: $value = static::detectDebugMode($value);
81: } elseif (!is_bool($value)) {
82: throw new Nette\InvalidArgumentException(sprintf('Value must be either a string, array, or boolean, %s given.', gettype($value)));
83: }
84: $this->parameters['debugMode'] = $value;
85: $this->parameters['productionMode'] = !$this->parameters['debugMode'];
86: return $this;
87: }
88:
89:
90: 91: 92:
93: public function isDebugMode()
94: {
95: return $this->parameters['debugMode'];
96: }
97:
98:
99: 100: 101: 102:
103: public function setTempDirectory($path)
104: {
105: $this->parameters['tempDir'] = $path;
106: return $this;
107: }
108:
109:
110: 111: 112: 113:
114: public function setTimeZone($timezone)
115: {
116: date_default_timezone_set($timezone);
117: @ini_set('date.timezone', $timezone);
118: return $this;
119: }
120:
121:
122: 123: 124: 125:
126: public function addParameters(array $params)
127: {
128: $this->parameters = DI\Config\Helpers::merge($params, $this->parameters);
129: return $this;
130: }
131:
132:
133: 134: 135: 136:
137: public function addServices(array $services)
138: {
139: $this->services = $services + $this->services;
140: return $this;
141: }
142:
143:
144: 145: 146:
147: protected function getDefaultParameters()
148: {
149: $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
150: $last = end($trace);
151: $debugMode = static::detectDebugMode();
152: return [
153: 'appDir' => isset($trace[1]['file']) ? dirname($trace[1]['file']) : NULL,
154: 'wwwDir' => isset($last['file']) ? dirname($last['file']) : NULL,
155: 'debugMode' => $debugMode,
156: 'productionMode' => !$debugMode,
157: 'consoleMode' => PHP_SAPI === 'cli',
158: ];
159: }
160:
161:
162: 163: 164: 165: 166:
167: public function enableTracy($logDirectory = NULL, $email = NULL)
168: {
169: $this->enableDebugger($logDirectory, $email);
170: }
171:
172:
173: 174: 175:
176: public function enableDebugger($logDirectory = NULL, $email = NULL)
177: {
178: Tracy\Debugger::$strictMode = TRUE;
179: Tracy\Debugger::enable(!$this->parameters['debugMode'], $logDirectory, $email);
180: Nette\Bridges\Framework\TracyBridge::initialize();
181: }
182:
183:
184: 185: 186: 187:
188: public function createRobotLoader()
189: {
190: if (!class_exists(Nette\Loaders\RobotLoader::class)) {
191: throw new Nette\NotSupportedException('RobotLoader not found, do you have `nette/robot-loader` package installed?');
192: }
193:
194: $loader = new Nette\Loaders\RobotLoader;
195: $loader->setCacheStorage(new Nette\Caching\Storages\FileStorage($this->getCacheDirectory()));
196: $loader->autoRebuild = $this->parameters['debugMode'];
197: return $loader;
198: }
199:
200:
201: 202: 203: 204:
205: public function addConfig($file)
206: {
207: $section = func_num_args() > 1 ? func_get_arg(1) : NULL;
208: if ($section !== NULL) {
209: trigger_error('Sections in config file are deprecated.', E_USER_DEPRECATED);
210: }
211: $this->files[] = [$file, $section === self::AUTO ? ($this->parameters['debugMode'] ? 'development' : 'production') : $section];
212: return $this;
213: }
214:
215:
216: 217: 218: 219:
220: public function createContainer()
221: {
222: $class = $this->loadContainer();
223: $container = new $class();
224: foreach ($this->services as $name => $service) {
225: $container->addService($name, $service);
226: }
227: $container->initialize();
228: if (class_exists(Nette\Environment::class)) {
229: Nette\Environment::setContext($container);
230: }
231: return $container;
232: }
233:
234:
235: 236: 237: 238:
239: public function loadContainer()
240: {
241: $loader = new DI\ContainerLoader(
242: $this->getCacheDirectory() . '/Nette.Configurator',
243: $this->parameters['debugMode']
244: );
245: $class = $loader->load(
246: [$this, 'generateContainer'],
247: [$this->parameters, $this->files, PHP_VERSION_ID - PHP_RELEASE_VERSION]
248: );
249: return $class;
250: }
251:
252:
253: 254: 255: 256:
257: public function generateContainer(DI\Compiler $compiler)
258: {
259: $loader = $this->createLoader();
260: $compiler->addConfig(['parameters' => $this->parameters]);
261: $fileInfo = [];
262: foreach ($this->files as $info) {
263: if (is_scalar($info[0])) {
264: $fileInfo[] = "// source: $info[0] $info[1]";
265: $info[0] = $loader->load($info[0], $info[1]);
266: }
267: $compiler->addConfig($this->fixCompatibility($info[0]));
268: }
269: $compiler->addDependencies($loader->getDependencies());
270:
271: $builder = $compiler->getContainerBuilder();
272: $builder->addExcludedClasses($this->autowireExcludedClasses);
273:
274: foreach ($this->defaultExtensions as $name => $extension) {
275: list($class, $args) = is_string($extension) ? [$extension, []] : $extension;
276: if (class_exists($class)) {
277: $args = DI\Helpers::expand($args, $this->parameters, TRUE);
278: $compiler->addExtension($name, (new \ReflectionClass($class))->newInstanceArgs($args));
279: }
280: }
281:
282: $this->onCompile($this, $compiler);
283:
284: $classes = $compiler->compile();
285: return implode("\n", $fileInfo) . "\n\n" . $classes;
286: }
287:
288:
289: 290: 291:
292: protected function createLoader()
293: {
294: return new DI\Config\Loader;
295: }
296:
297:
298: protected function getCacheDirectory()
299: {
300: if (empty($this->parameters['tempDir'])) {
301: throw new Nette\InvalidStateException('Set path to temporary directory using setTempDirectory().');
302: }
303: $dir = $this->parameters['tempDir'] . '/cache';
304: if (!is_dir($dir)) {
305: @mkdir($dir);
306: }
307: return $dir;
308: }
309:
310:
311: 312: 313: 314:
315: protected function fixCompatibility($config)
316: {
317: if (isset($config['nette']['security']['frames'])) {
318: $config['nette']['http']['frames'] = $config['nette']['security']['frames'];
319: unset($config['nette']['security']['frames']);
320: }
321: foreach (['application', 'cache', 'database', 'di' => 'container', 'forms', 'http',
322: 'latte', 'mail' => 'mailer', 'routing', 'security', 'session', 'tracy' => 'debugger'] as $new => $old) {
323: if (isset($config['nette'][$old])) {
324: $new = is_int($new) ? $old : $new;
325: if (isset($config[$new])) {
326: throw new Nette\DeprecatedException("You can use (deprecated) section 'nette.$old' or new section '$new', but not both of them.");
327: } else {
328: trigger_error("Configuration section 'nette.$old' is deprecated, use section '$new' (without 'nette')", E_USER_DEPRECATED);
329: }
330: $config[$new] = $config['nette'][$old];
331: unset($config['nette'][$old]);
332: }
333: }
334: if (isset($config['nette']['xhtml'])) {
335: trigger_error("Configuration option 'nette.xhtml' is deprecated, use section 'latte.xhtml' instead.", E_USER_DEPRECATED);
336: $config['latte']['xhtml'] = $config['nette']['xhtml'];
337: unset($config['nette']['xhtml']);
338: }
339:
340: if (empty($config['nette'])) {
341: unset($config['nette']);
342: }
343: return $config;
344: }
345:
346:
347:
348:
349:
350: 351: 352: 353: 354:
355: public static function detectDebugMode($list = NULL)
356: {
357: $addr = isset($_SERVER['REMOTE_ADDR'])
358: ? $_SERVER['REMOTE_ADDR']
359: : php_uname('n');
360: $secret = isset($_COOKIE[self::COOKIE_SECRET]) && is_string($_COOKIE[self::COOKIE_SECRET])
361: ? $_COOKIE[self::COOKIE_SECRET]
362: : NULL;
363: $list = is_string($list)
364: ? preg_split('#[,\s]+#', $list)
365: : (array) $list;
366: if (!isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
367: $list[] = '127.0.0.1';
368: $list[] = '::1';
369: }
370: return in_array($addr, $list, TRUE) || in_array("$secret@$addr", $list, TRUE);
371: }
372:
373: }
374: