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 extends Object
19: {
20: const AUTO = TRUE,
21: NONE = FALSE;
22:
23: const COOKIE_SECRET = 'nette-debug';
24:
25:
26: public $onCompile;
27:
28:
29: public $defaultExtensions = array(
30: 'php' => 'Nette\DI\Extensions\PhpExtension',
31: 'constants' => 'Nette\DI\Extensions\ConstantsExtension',
32: 'extensions' => 'Nette\DI\Extensions\ExtensionsExtension',
33: 'application' => array('Nette\Bridges\ApplicationDI\ApplicationExtension', array('%debugMode%', array('%appDir%'), '%tempDir%/cache')),
34: 'decorator' => 'Nette\DI\Extensions\DecoratorExtension',
35: 'cache' => array('Nette\Bridges\CacheDI\CacheExtension', array('%tempDir%')),
36: 'database' => array('Nette\Bridges\DatabaseDI\DatabaseExtension', array('%debugMode%')),
37: 'di' => array('Nette\DI\Extensions\DIExtension', array('%debugMode%')),
38: 'forms' => 'Nette\Bridges\FormsDI\FormsExtension',
39: 'http' => 'Nette\Bridges\HttpDI\HttpExtension',
40: 'latte' => array('Nette\Bridges\ApplicationDI\LatteExtension', array('%tempDir%/cache/latte', '%debugMode%')),
41: 'mail' => 'Nette\Bridges\MailDI\MailExtension',
42: 'reflection' => array('Nette\Bridges\ReflectionDI\ReflectionExtension', array('%debugMode%')),
43: 'routing' => array('Nette\Bridges\ApplicationDI\RoutingExtension', array('%debugMode%')),
44: 'security' => array('Nette\Bridges\SecurityDI\SecurityExtension', array('%debugMode%')),
45: 'session' => array('Nette\Bridges\HttpDI\SessionExtension', array('%debugMode%')),
46: 'tracy' => array('Tracy\Bridges\Nette\TracyExtension', array('%debugMode%')),
47: 'inject' => 'Nette\DI\Extensions\InjectExtension',
48: );
49:
50:
51: public $autowireExcludedClasses = array(
52: 'stdClass',
53: );
54:
55:
56: protected $parameters;
57:
58:
59: protected $services = array();
60:
61:
62: protected $files = array();
63:
64:
65: public function __construct()
66: {
67: $this->parameters = $this->getDefaultParameters();
68: }
69:
70:
71: 72: 73: 74: 75:
76: public function setDebugMode($value)
77: {
78: if (is_string($value) || is_array($value)) {
79: $value = static::detectDebugMode($value);
80: } elseif (!is_bool($value)) {
81: throw new Nette\InvalidArgumentException(sprintf('Value must be either a string, array, or boolean, %s given.', gettype($value)));
82: }
83: $this->parameters['debugMode'] = $value;
84: $this->parameters['productionMode'] = !$this->parameters['debugMode'];
85: $this->parameters['environment'] = $this->parameters['debugMode'] ? 'development' : 'production';
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 addParameters(array $params)
115: {
116: $this->parameters = DI\Config\Helpers::merge($params, $this->parameters);
117: return $this;
118: }
119:
120:
121: 122: 123: 124:
125: public function addServices(array $services)
126: {
127: $this->services = $services + $this->services;
128: return $this;
129: }
130:
131:
132: 133: 134:
135: protected function getDefaultParameters()
136: {
137: $trace = debug_backtrace(PHP_VERSION_ID >= 50306 ? DEBUG_BACKTRACE_IGNORE_ARGS : FALSE);
138: $last = end($trace);
139: $debugMode = static::detectDebugMode();
140: return array(
141: 'appDir' => isset($trace[1]['file']) ? dirname($trace[1]['file']) : NULL,
142: 'wwwDir' => isset($last['file']) ? dirname($last['file']) : NULL,
143: 'debugMode' => $debugMode,
144: 'productionMode' => !$debugMode,
145: 'environment' => $debugMode ? 'development' : 'production',
146: 'consoleMode' => PHP_SAPI === 'cli',
147: 'container' => array(
148: 'class' => NULL,
149: 'parent' => NULL,
150: ),
151: );
152: }
153:
154:
155: 156: 157: 158: 159:
160: public function enableDebugger($logDirectory = NULL, $email = NULL)
161: {
162: Tracy\Debugger::$strictMode = TRUE;
163: Tracy\Debugger::enable(!$this->parameters['debugMode'], $logDirectory, $email);
164: Nette\Bridges\Framework\TracyBridge::initialize();
165: }
166:
167:
168: 169: 170: 171:
172: public function createRobotLoader()
173: {
174: if (!class_exists('Nette\Loaders\RobotLoader')) {
175: throw new Nette\NotSupportedException('RobotLoader not found, do you have `nette/robot-loader` package installed?');
176: }
177:
178: $loader = new Nette\Loaders\RobotLoader;
179: $loader->setCacheStorage(new Nette\Caching\Storages\FileStorage($this->getCacheDirectory()));
180: $loader->autoRebuild = $this->parameters['debugMode'];
181: return $loader;
182: }
183:
184:
185: 186: 187: 188:
189: public function addConfig($file, $section = NULL)
190: {
191: if ($section === NULL && is_string($file) && $this->parameters['debugMode']) {
192: try {
193: $loader = new DI\Config\Loader;
194: $loader->load($file, $this->parameters['environment']);
195: trigger_error("Config file '$file' has sections, call addConfig() with second parameter Configurator::AUTO.", E_USER_WARNING);
196: $section = $this->parameters['environment'];
197: } catch (\Exception $e) {
198: }
199: }
200: $this->files[] = array($file, $section === self::AUTO ? $this->parameters['environment'] : $section);
201: return $this;
202: }
203:
204:
205: 206: 207: 208:
209: public function createContainer()
210: {
211: $loader = new DI\ContainerLoader(
212: $this->getCacheDirectory() . '/Nette.Configurator',
213: $this->parameters['debugMode']
214: );
215: $class = $loader->load(
216: array($this->parameters, $this->files, PHP_VERSION_ID - PHP_RELEASE_VERSION),
217: array($this, 'generateContainer')
218: );
219:
220: $container = new $class;
221: foreach ($this->services as $name => $service) {
222: $container->addService($name, $service);
223: }
224: $container->initialize();
225: if (class_exists('Nette\Environment')) {
226: Nette\Environment::setContext($container);
227: }
228: return $container;
229: }
230:
231:
232: 233: 234: 235:
236: public function generateContainer(DI\Compiler $compiler)
237: {
238: $loader = $this->createLoader();
239: $compiler->addConfig(array('parameters' => $this->parameters));
240: $fileInfo = array();
241: foreach ($this->files as $info) {
242: if (is_scalar($info[0])) {
243: $fileInfo[] = "// source: $info[0] $info[1]";
244: $info[0] = $loader->load($info[0], $info[1]);
245: }
246: $compiler->addConfig($this->fixCompatibility($info[0]));
247: }
248: $compiler->addDependencies($loader->getDependencies());
249:
250: $builder = $compiler->getContainerBuilder();
251: $builder->addExcludedClasses($this->autowireExcludedClasses);
252:
253: foreach ($this->defaultExtensions as $name => $extension) {
254: list($class, $args) = is_string($extension) ? array($extension, array()) : $extension;
255: if (class_exists($class)) {
256: $rc = new \ReflectionClass($class);
257: $args = DI\Helpers::expand($args, $this->parameters, TRUE);
258: $compiler->addExtension($name, $args ? $rc->newInstanceArgs($args) : $rc->newInstance());
259: }
260: }
261:
262: $this->onCompile($this, $compiler);
263:
264: $classes = $compiler->compile();
265:
266: if (!empty($builder->parameters['container']['parent'])) {
267: $classes[0]->setExtends($builder->parameters['container']['parent']);
268: }
269:
270: return implode("\n", $fileInfo) . "\n\n" . implode("\n\n\n", $classes)
271: . (($tmp = $builder->parameters['container']['class']) ? "\nclass $tmp extends {$builder->getClassName()} {}\n" : '');
272: }
273:
274:
275: 276: 277:
278: protected function createLoader()
279: {
280: return new DI\Config\Loader;
281: }
282:
283:
284: protected function getCacheDirectory()
285: {
286: if (empty($this->parameters['tempDir'])) {
287: throw new Nette\InvalidStateException('Set path to temporary directory using setTempDirectory().');
288: }
289: $dir = $this->parameters['tempDir'] . '/cache';
290: if (!is_dir($dir)) {
291: @mkdir($dir);
292: }
293: return $dir;
294: }
295:
296:
297: 298: 299: 300:
301: protected function fixCompatibility($config)
302: {
303: if (isset($config['nette']['security']['frames'])) {
304: $config['nette']['http']['frames'] = $config['nette']['security']['frames'];
305: unset($config['nette']['security']['frames']);
306: }
307: foreach (array('application', 'cache', 'database', 'di' => 'container', 'forms', 'http',
308: 'latte', 'mail' => 'mailer', 'routing', 'security', 'session', 'tracy' => 'debugger') as $new => $old) {
309: if (isset($config['nette'][$old])) {
310: $new = is_int($new) ? $old : $new;
311: if (isset($config[$new])) {
312: throw new Nette\DeprecatedException("You can use (deprecated) section 'nette.$old' or new section '$new', but not both of them.");
313: }
314: $config[$new] = $config['nette'][$old];
315: unset($config['nette'][$old]);
316: }
317: }
318: if (isset($config['nette']['xhtml'])) {
319: trigger_error("Configuration option 'nette.xhtml' is deprecated, use section 'latte.xhtml' instead.", E_USER_DEPRECATED);
320: $config['latte']['xhtml'] = $config['nette']['xhtml'];
321: unset($config['nette']['xhtml']);
322: }
323:
324: if (empty($config['nette'])) {
325: unset($config['nette']);
326: }
327: return $config;
328: }
329:
330:
331:
332:
333:
334: 335: 336: 337: 338:
339: public static function detectDebugMode($list = NULL)
340: {
341: $addr = isset($_SERVER['REMOTE_ADDR'])
342: ? $_SERVER['REMOTE_ADDR']
343: : php_uname('n');
344: $secret = isset($_COOKIE[self::COOKIE_SECRET]) && is_string($_COOKIE[self::COOKIE_SECRET])
345: ? $_COOKIE[self::COOKIE_SECRET]
346: : NULL;
347: $list = is_string($list)
348: ? preg_split('#[,\s]+#', $list)
349: : (array) $list;
350: if (!isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
351: $list[] = '127.0.0.1';
352: $list[] = '::1';
353: }
354: return in_array($addr, $list, TRUE) || in_array("$secret@$addr", $list, TRUE);
355: }
356:
357: }
358: