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