1: <?php
2:
3: 4: 5: 6:
7:
8: namespace Nette\Bridges\Framework;
9:
10: use Nette,
11: Nette\DI\ContainerBuilder,
12: Nette\Utils\Validators,
13: Latte;
14:
15:
16: 17: 18: 19: 20:
21: class NetteExtension extends Nette\DI\CompilerExtension
22: {
23: public $defaults = array(
24: 'http' => array(
25: 'proxy' => array(),
26: 'headers' => array(
27: 'X-Powered-By' => 'Nette Framework',
28: 'Content-Type' => 'text/html; charset=utf-8',
29: ),
30: ),
31: 'session' => array(
32: 'debugger' => FALSE,
33: 'autoStart' => 'smart',
34: 'expiration' => NULL,
35: ),
36: 'application' => array(
37: 'debugger' => TRUE,
38: 'errorPresenter' => 'Nette:Error',
39: 'catchExceptions' => '%productionMode%',
40: 'mapping' => NULL
41: ),
42: 'routing' => array(
43: 'debugger' => TRUE,
44: 'routes' => array(),
45: ),
46: 'security' => array(
47: 'debugger' => TRUE,
48: 'frames' => 'SAMEORIGIN',
49: 'users' => array(),
50: 'roles' => array(),
51: 'resources' => array(),
52: ),
53: 'mailer' => array(
54: 'smtp' => FALSE,
55: 'host' => NULL,
56: 'port' => NULL,
57: 'username' => NULL,
58: 'password' => NULL,
59: 'secure' => NULL,
60: 'timeout' => NULL,
61: ),
62: 'database' => array(),
63: 'forms' => array(
64: 'messages' => array(),
65: ),
66: 'latte' => array(
67: 'xhtml' => FALSE,
68: 'macros' => array(),
69: ),
70: 'container' => array(
71: 'debugger' => FALSE,
72: 'accessors' => TRUE,
73: ),
74: 'debugger' => array(
75: 'email' => NULL,
76: 'editor' => NULL,
77: 'browser' => NULL,
78: 'strictMode' => NULL,
79: 'maxLen' => NULL,
80: 'maxDepth' => NULL,
81: 'showLocation' => NULL,
82: 'scream' => NULL,
83: 'bar' => array(),
84: 'blueScreen' => array(),
85: ),
86: );
87:
88:
89: public function loadConfiguration()
90: {
91: $container = $this->getContainerBuilder();
92: $config = $this->getConfig($this->defaults);
93:
94: if (isset($config['xhtml'])) {
95: $config['latte']['xhtml'] = $config['xhtml'];
96: unset($config['xhtml']);
97: }
98: $container->addDefinition('nette')->setClass('Nette\Bridges\Framework\NetteAccessor', array('@container'));
99:
100: $this->validate($config, $this->defaults, 'nette');
101:
102: $this->setupCache($container);
103: $this->setupHttp($container, $config['http']);
104: $this->setupSession($container, $config['session']);
105: $this->setupSecurity($container, $config['security']);
106: $this->setupApplication($container, $config['application']);
107: $this->setupRouting($container, $config['routing']);
108: $this->setupMailer($container, $config['mailer']);
109: $this->setupLatte($container, $config['latte']);
110: $this->setupContainer($container, $config['container']);
111: }
112:
113:
114: private function setupCache(ContainerBuilder $container)
115: {
116: $container->addDefinition($this->prefix('cacheJournal'))
117: ->setClass('Nette\Caching\Storages\FileJournal', array($container->expand('%tempDir%')));
118:
119: $container->addDefinition('cacheStorage')
120: ->setClass('Nette\Caching\Storages\FileStorage', array($container->expand('%tempDir%/cache')));
121:
122: if (class_exists('Nette\Caching\Storages\PhpFileStorage')) {
123: $container->addDefinition($this->prefix('templateCacheStorage'))
124: ->setClass('Nette\Caching\Storages\PhpFileStorage', array($container->expand('%tempDir%/cache')))
125: ->addSetup('::trigger_error', array('Service templateCacheStorage is deprecated.', E_USER_DEPRECATED))
126: ->setAutowired(FALSE);
127: }
128:
129: $container->addDefinition($this->prefix('cache'))
130: ->setClass('Nette\Caching\Cache', array(1 => $container::literal('$namespace')))
131: ->addSetup('::trigger_error', array('Service cache is deprecated.', E_USER_DEPRECATED))
132: ->setParameters(array('namespace' => NULL))
133: ->setAutowired(FALSE);
134: }
135:
136:
137: private function setupHttp(ContainerBuilder $container, array $config)
138: {
139: $this->validate($config, $this->defaults['http'], 'nette.http');
140:
141: $container->addDefinition($this->prefix('httpRequestFactory'))
142: ->setClass('Nette\Http\RequestFactory')
143: ->addSetup('setProxy', array($config['proxy']));
144:
145: $container->addDefinition('httpRequest')
146: ->setClass('Nette\Http\Request')
147: ->setFactory('@Nette\Http\RequestFactory::createHttpRequest');
148:
149: $container->addDefinition('httpResponse')
150: ->setClass('Nette\Http\Response');
151:
152: $container->addDefinition($this->prefix('httpContext'))
153: ->setClass('Nette\Http\Context');
154: }
155:
156:
157: private function setupSession(ContainerBuilder $container, array $config)
158: {
159: $session = $container->addDefinition('session')
160: ->setClass('Nette\Http\Session');
161:
162: if (isset($config['expiration'])) {
163: $session->addSetup('setExpiration', array($config['expiration']));
164: }
165:
166: if ($container->parameters['debugMode'] && $config['debugger']) {
167: $session->addSetup('Tracy\Debugger::getBar()->addPanel(?)', array(
168: new Nette\DI\Statement('Nette\Bridges\HttpTracy\SessionPanel')
169: ));
170: }
171:
172: unset($config['expiration'], $config['autoStart'], $config['debugger']);
173: if (!empty($config)) {
174: $session->addSetup('setOptions', array($config));
175: }
176: }
177:
178:
179: private function setupSecurity(ContainerBuilder $container, array $config)
180: {
181: $this->validate($config, $this->defaults['security'], 'nette.security');
182:
183: $container->addDefinition($this->prefix('userStorage'))
184: ->setClass('Nette\Http\UserStorage');
185:
186: $user = $container->addDefinition('user')
187: ->setClass('Nette\Security\User');
188:
189: if ($container->parameters['debugMode'] && $config['debugger']) {
190: $user->addSetup('Tracy\Debugger::getBar()->addPanel(?)', array(
191: new Nette\DI\Statement('Nette\Bridges\SecurityTracy\UserPanel')
192: ));
193: }
194:
195: if ($config['users']) {
196: $usersList = $usersRoles = array();
197: foreach ($config['users'] as $username => $data) {
198: $usersList[$username] = is_array($data) ? $data['password'] : $data;
199: $usersRoles[$username] = is_array($data) && isset($data['roles']) ? $data['roles'] : NULL;
200: }
201:
202: $container->addDefinition($this->prefix('authenticator'))
203: ->setClass('Nette\Security\SimpleAuthenticator', array($usersList, $usersRoles));
204: }
205:
206: if ($config['roles'] || $config['resources']) {
207: $authorizator = $container->addDefinition($this->prefix('authorizator'))
208: ->setClass('Nette\Security\Permission');
209: foreach ($config['roles'] as $role => $parents) {
210: $authorizator->addSetup('addRole', array($role, $parents));
211: }
212: foreach ($config['resources'] as $resource => $parents) {
213: $authorizator->addSetup('addResource', array($resource, $parents));
214: }
215: }
216: }
217:
218:
219: private function setupApplication(ContainerBuilder $container, array $config)
220: {
221: $this->validate($config, $this->defaults['application'], 'nette.application');
222:
223: $application = $container->addDefinition('application')
224: ->setClass('Nette\Application\Application')
225: ->addSetup('$catchExceptions', array($config['catchExceptions']))
226: ->addSetup('$errorPresenter', array($config['errorPresenter']));
227:
228: if ($config['debugger']) {
229: $application->addSetup('Nette\Bridges\ApplicationTracy\RoutingPanel::initializePanel');
230: }
231:
232: $presenterFactory = $container->addDefinition($this->prefix('presenterFactory'))
233: ->setClass('Nette\Application\PresenterFactory', array(
234: isset($container->parameters['appDir']) ? $container->parameters['appDir'] : NULL
235: ));
236: if ($config['mapping']) {
237: $presenterFactory->addSetup('setMapping', array($config['mapping']));
238: }
239: }
240:
241:
242: private function setupRouting(ContainerBuilder $container, array $config)
243: {
244: $this->validate($config, $this->defaults['routing'], 'nette.routing');
245:
246: $router = $container->addDefinition('router')
247: ->setClass('Nette\Application\Routers\RouteList');
248:
249: foreach ($config['routes'] as $mask => $action) {
250: $router->addSetup('$service[] = new Nette\Application\Routers\Route(?, ?);', array($mask, $action));
251: }
252:
253: if ($container->parameters['debugMode'] && $config['debugger']) {
254: $container->getDefinition('application')->addSetup('Tracy\Debugger::getBar()->addPanel(?)', array(
255: new Nette\DI\Statement('Nette\Bridges\ApplicationTracy\RoutingPanel')
256: ));
257: }
258: }
259:
260:
261: private function setupMailer(ContainerBuilder $container, array $config)
262: {
263: $this->validate($config, $this->defaults['mailer'], 'nette.mailer');
264:
265: if (empty($config['smtp'])) {
266: $container->addDefinition($this->prefix('mailer'))
267: ->setClass('Nette\Mail\SendmailMailer');
268: } else {
269: $container->addDefinition($this->prefix('mailer'))
270: ->setClass('Nette\Mail\SmtpMailer', array($config));
271: }
272: }
273:
274:
275: private function setupLatte(ContainerBuilder $container, array $config)
276: {
277: $this->validate($config, $this->defaults['latte'], 'nette.latte');
278:
279: $latteFactory = $container->addDefinition($this->prefix('latteFactory'))
280: ->setClass('Latte\Engine')
281: ->addSetup('setTempDirectory', array($container->expand('%tempDir%/cache/latte')))
282: ->addSetup('setAutoRefresh', array($container->parameters['debugMode']))
283: ->addSetup('setContentType', array($config['xhtml'] ? Latte\Compiler::CONTENT_XHTML : Latte\Compiler::CONTENT_HTML))
284: ->setImplement('Nette\Bridges\ApplicationLatte\ILatteFactory');
285:
286: $container->addDefinition($this->prefix('templateFactory'))
287: ->setClass('Nette\Bridges\ApplicationLatte\TemplateFactory');
288:
289: $latte = $container->addDefinition($this->prefix('latte'))
290: ->setClass('Latte\Engine')
291: ->addSetup('setTempDirectory', array($container->expand('%tempDir%/cache/latte')))
292: ->addSetup('setAutoRefresh', array($container->parameters['debugMode']))
293: ->addSetup('setContentType', array($config['xhtml'] ? Latte\Compiler::CONTENT_XHTML : Latte\Compiler::CONTENT_HTML))
294: ->setAutowired(FALSE);
295:
296: foreach ($config['macros'] as $macro) {
297: if (strpos($macro, '::') === FALSE && class_exists($macro)) {
298: $macro .= '::install';
299: } else {
300: Validators::assert($macro, 'callable');
301: }
302: $latte->addSetup('?->onCompile[] = function($engine) { ' . $macro . '($engine->getCompiler()); }', array('@self'));
303: $latteFactory->addSetup('?->onCompile[] = function($engine) { ' . $macro . '($engine->getCompiler()); }', array('@self'));
304: }
305:
306: if (class_exists('Nette\Templating\FileTemplate')) {
307: $container->addDefinition($this->prefix('template'))
308: ->setClass('Nette\Templating\FileTemplate')
309: ->addSetup('registerFilter', array(new Nette\DI\Statement(array($latteFactory, 'create'))))
310: ->addSetup('registerHelperLoader', array('Nette\Templating\Helpers::loader'))
311: ->setAutowired(FALSE);
312: }
313: }
314:
315:
316: private function setupContainer(ContainerBuilder $container, array $config)
317: {
318: $this->validate($config, $this->defaults['container'], 'nette.container');
319:
320: if ($config['accessors']) {
321: $container->parameters['container']['accessors'] = TRUE;
322: }
323: }
324:
325:
326: public function afterCompile(Nette\PhpGenerator\ClassType $class)
327: {
328: $initialize = $class->methods['initialize'];
329: $container = $this->getContainerBuilder();
330: $config = $this->getConfig($this->defaults);
331:
332:
333: $initialize->addBody('Nette\Bridges\Framework\TracyBridge::initialize();');
334:
335: foreach (array('email', 'editor', 'browser', 'strictMode', 'maxLen', 'maxDepth', 'showLocation', 'scream') as $key) {
336: if (isset($config['debugger'][$key])) {
337: $initialize->addBody('Tracy\Debugger::$? = ?;', array($key, $config['debugger'][$key]));
338: }
339: }
340:
341: if ($container->parameters['debugMode']) {
342: if ($config['container']['debugger']) {
343: $config['debugger']['bar'][] = 'Nette\Bridges\DITracy\ContainerPanel';
344: }
345:
346: foreach ((array) $config['debugger']['bar'] as $item) {
347: $initialize->addBody($container->formatPhp(
348: 'Tracy\Debugger::getBar()->addPanel(?);',
349: Nette\DI\Compiler::filterArguments(array(is_string($item) ? new Nette\DI\Statement($item) : $item))
350: ));
351: }
352: }
353:
354: foreach ((array) $config['debugger']['blueScreen'] as $item) {
355: $initialize->addBody($container->formatPhp(
356: 'Tracy\Debugger::getBlueScreen()->addPanel(?);',
357: Nette\DI\Compiler::filterArguments(array($item))
358: ));
359: }
360:
361: if (!empty($container->parameters['tempDir'])) {
362: $initialize->addBody('Nette\Caching\Storages\FileStorage::$useDirectories = ?;', array($this->checkTempDir($container->expand('%tempDir%/cache'))));
363: }
364:
365: foreach ((array) $config['forms']['messages'] as $name => $text) {
366: $initialize->addBody('Nette\Forms\Rules::$defaultMessages[Nette\Forms\Form::?] = ?;', array($name, $text));
367: }
368:
369: if ($config['session']['autoStart'] === 'smart') {
370: $initialize->addBody('$this->getByType("Nette\Http\Session")->exists() && $this->getByType("Nette\Http\Session")->start();');
371: } elseif ($config['session']['autoStart']) {
372: $initialize->addBody('$this->getByType("Nette\Http\Session")->start();');
373: }
374:
375: if ($config['latte']['xhtml']) {
376: $initialize->addBody('Nette\Utils\Html::$xhtml = ?;', array(TRUE));
377: }
378:
379: if (isset($config['security']['frames']) && $config['security']['frames'] !== TRUE) {
380: $frames = $config['security']['frames'];
381: if ($frames === FALSE) {
382: $frames = 'DENY';
383: } elseif (preg_match('#^https?:#', $frames)) {
384: $frames = "ALLOW-FROM $frames";
385: }
386: $initialize->addBody('header(?);', array("X-Frame-Options: $frames"));
387: }
388:
389: foreach ($container->findByTag('run') as $name => $on) {
390: if ($on) {
391: $initialize->addBody('$this->getService(?);', array($name));
392: }
393: }
394:
395: if (!empty($config['container']['accessors'])) {
396: $definitions = $container->definitions;
397: ksort($definitions);
398: foreach ($definitions as $name => $def) {
399: if (Nette\PhpGenerator\Helpers::isIdentifier($name)) {
400: $type = $def->implement ?: $def->class;
401: $class->addDocument("@property $type \$$name");
402: }
403: }
404: }
405:
406: foreach ($config['http']['headers'] as $key => $value) {
407: if ($value != NULL) {
408: $initialize->addBody('header(?);', array("$key: $value"));
409: }
410: }
411:
412: $initialize->addBody('Nette\Utils\SafeStream::register();');
413: $initialize->addBody('Nette\Reflection\AnnotationsParser::setCacheStorage($this->getByType("Nette\Caching\IStorage"));');
414: $initialize->addBody('Nette\Reflection\AnnotationsParser::$autoRefresh = ?;', array($container->parameters['debugMode']));
415: }
416:
417:
418: private function checkTempDir($dir)
419: {
420:
421: $uniq = uniqid('_', TRUE);
422: if (!@mkdir("$dir/$uniq")) {
423: throw new Nette\InvalidStateException("Unable to write to directory '$dir'. Make this directory writable.");
424: }
425:
426:
427: $isWritable = @file_put_contents("$dir/$uniq/_", '') !== FALSE;
428: if ($isWritable) {
429: unlink("$dir/$uniq/_");
430: }
431: rmdir("$dir/$uniq");
432: return $isWritable;
433: }
434:
435:
436: private function validate(array $config, array $expected, $name)
437: {
438: if ($extra = array_diff_key($config, $expected)) {
439: $extra = implode(", $name.", array_keys($extra));
440: throw new Nette\InvalidStateException("Unknown option $name.$extra.");
441: }
442: }
443:
444: }
445: