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