1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10: 11:
12:
13:
14:
15: 16: 17: 18: 19: 20:
21: class Configurator extends Object
22: {
23: public static $instance;
24:
25:
26: public $defaultConfigFile = '%appDir%/config.neon';
27:
28:
29: private $container;
30:
31:
32:
33: public function __construct($containerClass = 'DIContainer')
34: {
35: self::$instance = $this;
36: $this->container = new $containerClass;
37: $this->container->addService('container', $this->container);
38:
39: foreach (get_class_methods($this) as $name) {
40: if (substr($name, 0, 13) === 'createService' ) {
41: $this->container->addService(strtolower($name[13]) . substr($name, 14), array(__CLASS__, $name));
42: }
43: }
44:
45: defined('WWW_DIR') && $this->container->params['wwwDir'] = realpath(WWW_DIR);
46: defined('APP_DIR') && $this->container->params['appDir'] = realpath(APP_DIR);
47: defined('LIBS_DIR') && $this->container->params['libsDir'] = realpath(LIBS_DIR);
48: defined('TEMP_DIR') && $this->container->params['tempDir'] = realpath(TEMP_DIR);
49: $this->container->params['productionMode'] = self::detectProductionMode();
50: $this->container->params['consoleMode'] = PHP_SAPI === 'cli';
51: }
52:
53:
54:
55: 56: 57: 58:
59: public function getContainer()
60: {
61: return $this->container;
62: }
63:
64:
65:
66: 67: 68: 69:
70: public function loadConfig($file, $section = NULL)
71: {
72: if ($file === NULL) {
73: $file = $this->defaultConfigFile;
74: }
75: $container = $this->container;
76: $file = $container->expand($file);
77: if (!is_file($file)) {
78: $file = preg_replace('#\.neon$#', '.ini', $file);
79: }
80: if ($section === NULL) {
81: if (PHP_SAPI === 'cli') {
82: $section = Environment::CONSOLE;
83: } else {
84: $section = $container->params['productionMode'] ? Environment::PRODUCTION : Environment::DEVELOPMENT;
85: }
86: }
87:
88: $cache = new Cache($container->templateCacheStorage, 'Nette.Configurator');
89: $cacheKey = array((array) $container->params, $file, $section);
90: $cached = $cache->load($cacheKey);
91: if ($cached) {
92: require $cached['file'];
93: fclose($cached['handle']);
94: return $this->container;
95: }
96:
97: $config = Config::fromFile($file, $section);
98: $code = "<?php\n// source file $file\n\n";
99:
100:
101: foreach (array('service', 'variable') as $item) {
102: if (isset($config[$item])) {
103: trigger_error(basename($file) . ": Section '$item' is deprecated; use plural form '{$item}s' instead.", E_USER_WARNING);
104: $config[$item . 's'] = $config[$item];
105: unset($config[$item]);
106: }
107: }
108:
109:
110: if (isset($config['services'])) {
111: foreach ($config['services'] as $key => & $def) {
112: if (preg_match('#^Nette\\\\.*\\\\I?([a-zA-Z]+)$#', strtr($key, '-', '\\'), $m)) {
113: $m[1][0] = strtolower($m[1][0]);
114: trigger_error(basename($file) . ": service name '$key' has been renamed to '$m[1]'", E_USER_WARNING);
115: $key = $m[1];
116: }
117:
118: if (is_array($def)) {
119: if (method_exists(__CLASS__, "createService$key") && !isset($def['factory']) && !isset($def['class'])) {
120: $def['factory'] = array(__CLASS__, "createService$key");
121: }
122:
123: if (isset($def['option'])) {
124: $def['arguments'][] = $def['option'];
125: }
126:
127: if (!empty($def['run'])) {
128: $def['tags'] = array('run');
129: }
130: }
131: }
132: $builder = new DIContainerBuilder;
133: $code .= '$builder = new '.get_class($builder).'; $builder->addDefinitions($container, '.var_export($config['services'], TRUE).');';
134: unset($config['services']);
135: }
136:
137:
138: if (!isset($config['variables'])) {
139: $config['variables'] = array();
140: }
141: foreach ($config as $key => $value) {
142: if (!in_array($key, array('variables', 'services', 'php', 'const', 'mode'))) {
143: $config['variables'][$key] = $value;
144: }
145: }
146:
147:
148: $variables = $config['variables'];
149: array_walk_recursive($config, create_function('&$val', 'extract(NCFix::$vars['.NCFix::uses(array('variables'=>$variables)).'], EXTR_REFS);
150: $val = Configurator::preExpand($val, $variables);
151: '));
152:
153:
154: foreach ($config['variables'] as $key => $value) {
155: $code .= $this->generateCode('$container->params[?] = ?', $key, $value);
156: }
157:
158:
159: if (isset($config['php'])) {
160: foreach ($config['php'] as $key => $value) {
161: if (is_array($value)) {
162: foreach ($value as $k => $v) {
163: $code .= $this->configurePhp("$key.$k", $v);
164: }
165: } else {
166: $code .= $this->configurePhp($key, $value);
167: }
168: }
169: }
170:
171:
172: if (isset($config['const'])) {
173: foreach ($config['const'] as $key => $value) {
174: $code .= $this->generateCode('define', $key, $value);
175: }
176: }
177:
178:
179: if (isset($config['mode'])) {
180: foreach ($config['mode'] as $mode => $state) {
181: trigger_error(basename($file) . ": Section 'mode' is deprecated; use '{$mode}Mode' in section 'variables' instead.", E_USER_WARNING);
182: $code .= $this->generateCode('$container->params[?] = ?', $mode . 'Mode', (bool) $state);
183: }
184: }
185:
186:
187: $code .= self::preloadEnvironment($container);
188:
189:
190: $code .= 'foreach ($container->getServiceNamesByTag("run") as $name => $foo) { $container->getService($name); }' . "\n";
191:
192: $cache->save($cacheKey, $code, array(
193: Cache::FILES => $file,
194: ));
195:
196: LimitedScope::evaluate($code, array('container' => $container));
197: return $this->container;
198: }
199:
200:
201:
202:
203:
204:
205:
206: 207: 208: 209:
210: public static function detectProductionMode()
211: {
212: $addrs = array();
213: if (PHP_SAPI === 'cli') {
214: $addrs[] = getHostByName(php_uname('n'));
215: }
216: else {
217: if (!isset($_SERVER['SERVER_ADDR']) && !isset($_SERVER['LOCAL_ADDR'])) {
218: return TRUE;
219: }
220: if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
221: $addrs = preg_split('#,\s*#', $_SERVER['HTTP_X_FORWARDED_FOR']);
222: }
223: if (isset($_SERVER['REMOTE_ADDR'])) {
224: $addrs[] = $_SERVER['REMOTE_ADDR'];
225: }
226: $addrs[] = isset($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : $_SERVER['LOCAL_ADDR'];
227: }
228: foreach ($addrs as $addr) {
229: $oct = explode('.', $addr);
230:
231:
232:
233:
234:
235: if ($addr !== '::1' && (count($oct) !== 4 || ($oct[0] !== '10' && $oct[0] !== '127' && ($oct[0] !== '172' || $oct[1] < 16 || $oct[1] > 31)
236: && ($oct[0] !== '169' || $oct[1] !== '254') && ($oct[0] !== '192' || $oct[1] !== '168')))
237: ) {
238: return TRUE;
239: }
240: }
241: return FALSE;
242: }
243:
244:
245:
246: public function configurePhp($name, $value)
247: {
248: if (!is_scalar($value)) {
249: throw new InvalidStateException("Configuration value for directive '$name' is not scalar.");
250: }
251:
252: switch ($name) {
253: case 'include_path':
254: return $this->generateCode('set_include_path', str_replace(';', PATH_SEPARATOR, $value));
255: case 'ignore_user_abort':
256: return $this->generateCode('ignore_user_abort', $value);
257: case 'max_execution_time':
258: return $this->generateCode('set_time_limit', $value);
259: case 'date.timezone':
260: return $this->generateCode('date_default_timezone_set', $value);
261: }
262:
263: if (function_exists('ini_set')) {
264: return $this->generateCode('ini_set', $name, $value);
265: } elseif (ini_get($name) != $value && !Framework::$iAmUsingBadHost) {
266: throw new NotSupportedException('Required function ini_set() is disabled.');
267: }
268: }
269:
270:
271:
272: private static function generateCode($statement)
273: {
274: $args = func_get_args();
275: unset($args[0]);
276: foreach ($args as &$arg) {
277: $arg = var_export($arg, TRUE);
278: $arg = preg_replace("#(?<!\\\)'%([\w-]+)%'#", '\$container->params[\'$1\']', $arg);
279: $arg = preg_replace("#(?<!\\\)'(?:[^'\\\]|\\\.)*%(?:[^'\\\]|\\\.)*'#", '\$container->expand($0)', $arg);
280: }
281: if (strpos($statement, '?') === FALSE) {
282: return $statement .= '(' . implode(', ', $args) . ");\n\n";
283: }
284: $a = strpos($statement, '?');
285: $i = 1;
286: while ($a !== FALSE) {
287: $statement = substr_replace($statement, $args[$i], $a, 1);
288: $a = strpos($statement, '?', $a + strlen($args[$i]));
289: $i++;
290: }
291: return $statement . ";\n\n";
292: }
293:
294:
295:
296: 297: 298: 299:
300: public static function preExpand($s, array $params, $check = array())
301: {
302: if (!is_string($s)) {
303: return $s;
304: }
305:
306: $parts = preg_split('#%([\w.-]*)%#i', $s, -1, PREG_SPLIT_DELIM_CAPTURE);
307: $res = '';
308: foreach ($parts as $n => $part) {
309: if ($n % 2 === 0) {
310: $res .= str_replace('%', '%%', $part);
311:
312: } elseif ($part === '') {
313: $res .= '%%';
314:
315: } elseif (isset($check[$part])) {
316: throw new InvalidArgumentException('Circular reference detected for variables: ' . implode(', ', array_keys($check)) . '.');
317:
318: } else {
319: try {
320: $val = Arrays::get($params, explode('.', $part));
321: } catch (InvalidArgumentException $e) {
322: $res .= "%$part%";
323: continue;
324: }
325: $val = self::preExpand($val, $params, $check + array($part => 1));
326: if (strlen($part) + 2 === strlen($s)) {
327: if (is_array($val)) {
328: array_walk_recursive($val, create_function('&$val', 'extract(NCFix::$vars['.NCFix::uses(array('params'=>$params,'check'=> $check,'part'=> $part)).'], EXTR_REFS);
329: $val = Configurator::preExpand($val, $params, $check + array($part => 1));
330: '));
331: }
332: return $val;
333: }
334: if (!is_scalar($val)) {
335: throw new InvalidArgumentException("Unable to concatenate non-scalar parameter '$part' into '$s'.");
336: }
337: $res .= $val;
338: }
339: }
340: return $res;
341: }
342:
343:
344:
345:
346:
347:
348:
349: 350: 351:
352: public static function createServiceApplication(DIContainer $container, array $options = NULL)
353: {
354: $context = new DIContainer;
355: $context->addService('httpRequest', $container->httpRequest);
356: $context->addService('httpResponse', $container->httpResponse);
357: $context->addService('session', $container->session);
358: $context->addService('presenterFactory', $container->presenterFactory);
359: $context->addService('router', $container->router);
360:
361: Presenter::$invalidLinkMode = $container->params['productionMode']
362: ? Presenter::INVALID_LINK_SILENT
363: : Presenter::INVALID_LINK_WARNING;
364:
365: $class = isset($options['class']) ? $options['class'] : 'Application';
366: $application = new $class($context);
367: $application->catchExceptions = $container->params['productionMode'];
368: if ($container->session->exists()) {
369: $application->onStartup[] = create_function('', 'extract(NCFix::$vars['.NCFix::uses(array('container'=>$container)).'], EXTR_REFS);
370: $container->session->start(); // opens already started session
371: ');
372: }
373: return $application;
374: }
375:
376:
377:
378: 379: 380:
381: public static function createServicePresenterFactory(DIContainer $container)
382: {
383: return new PresenterFactory(
384: isset($container->params['appDir']) ? $container->params['appDir'] : NULL,
385: $container
386: );
387: }
388:
389:
390:
391: 392: 393:
394: public static function createServiceRouter(DIContainer $container)
395: {
396: return new RouteList;
397: }
398:
399:
400:
401: 402: 403:
404: public static function createServiceHttpRequest()
405: {
406: $factory = new HttpRequestFactory;
407: $factory->setEncoding('UTF-8');
408: return $factory->createHttpRequest();
409: }
410:
411:
412:
413: 414: 415:
416: public static function createServiceHttpResponse()
417: {
418: return new HttpResponse;
419: }
420:
421:
422:
423: 424: 425:
426: public static function createServiceHttpContext(DIContainer $container)
427: {
428: return new HttpContext($container->httpRequest, $container->httpResponse);
429: }
430:
431:
432:
433: 434: 435:
436: public static function createServiceSession(DIContainer $container, array $options = NULL)
437: {
438: $session = new Session($container->httpRequest, $container->httpResponse);
439: $session->setOptions((array) $options);
440: if (isset($options['expiration'])) {
441: $session->setExpiration($options['expiration']);
442: }
443: return $session;
444: }
445:
446:
447:
448: 449: 450:
451: public static function createServiceUser(DIContainer $container)
452: {
453: $context = new DIContainer;
454:
455: $context->addService('authenticator', create_function('', 'extract(NCFix::$vars['.NCFix::uses(array('container'=>$container)).'], EXTR_REFS);
456: return $container->authenticator;
457: '));
458: $context->addService('authorizator', create_function('', 'extract(NCFix::$vars['.NCFix::uses(array('container'=>$container)).'], EXTR_REFS);
459: return $container->authorizator;
460: '));
461: $context->addService('session', $container->session);
462: return new User($context);
463: }
464:
465:
466:
467: 468: 469:
470: public static function createServiceCacheStorage(DIContainer $container)
471: {
472: if (!isset($container->params['tempDir'])) {
473: throw new InvalidStateException("Service cacheStorage requires that parameter 'tempDir' contains path to temporary directory.");
474: }
475: $dir = $container->expand('%tempDir%/cache');
476: umask(0000);
477: @mkdir($dir, 0777);
478: return new FileStorage($dir, $container->cacheJournal);
479: }
480:
481:
482:
483: 484: 485:
486: public static function createServiceTemplateCacheStorage(DIContainer $container)
487: {
488: if (!isset($container->params['tempDir'])) {
489: throw new InvalidStateException("Service templateCacheStorage requires that parameter 'tempDir' contains path to temporary directory.");
490: }
491: $dir = $container->expand('%tempDir%/cache');
492: umask(0000);
493: @mkdir($dir, 0777);
494: return new PhpFileStorage($dir);
495: }
496:
497:
498:
499: 500: 501:
502: public static function createServiceCacheJournal(DIContainer $container)
503: {
504: return new FileJournal($container->params['tempDir']);
505: }
506:
507:
508:
509: 510: 511:
512: public static function createServiceMailer(DIContainer $container, array $options = NULL)
513: {
514: if (empty($options['smtp'])) {
515: return new SendmailMailer;
516: } else {
517: return new SmtpMailer($options);
518: }
519: }
520:
521:
522:
523: 524: 525:
526: public static function createServiceRobotLoader(DIContainer $container, array $options = NULL)
527: {
528: $loader = new RobotLoader;
529: $loader->autoRebuild = isset($options['autoRebuild']) ? $options['autoRebuild'] : !$container->params['productionMode'];
530: $loader->setCacheStorage($container->cacheStorage);
531: if (isset($options['directory'])) {
532: $loader->addDirectory($options['directory']);
533: } else {
534: foreach (array('appDir', 'libsDir') as $var) {
535: if (isset($container->params[$var])) {
536: $loader->addDirectory($container->params[$var]);
537: }
538: }
539: }
540: $loader->register();
541: return $loader;
542: }
543:
544:
545:
546: public static function preloadEnvironment(DIContainer $container)
547: {
548: $code = '';
549: $dir = $container->expand('%tempDir%/cache');
550: umask(0000);
551: @mkdir($dir, 0777);
552:
553:
554: $uniq = uniqid('_', TRUE);
555: umask(0000);
556: if (!@mkdir("$dir/$uniq", 0777)) {
557: throw new InvalidStateException("Unable to write to directory '$dir'. Make this directory writable.");
558: }
559:
560:
561: $useDirs = @file_put_contents("$dir/$uniq/_", '') !== FALSE;
562: @unlink("$dir/$uniq/_");
563: @rmdir("$dir/$uniq");
564:
565: $code .= self::generateCode('FileStorage::$useDirectories = ?', $useDirs);
566: return $code;
567: }
568:
569: }
570: