1: <?php
2:
3: 4: 5: 6:
7:
8: namespace Nette\Bridges\ApplicationDI;
9:
10: use Nette,
11: Nette\Application\UI;
12:
13:
14: 15: 16: 17: 18:
19: class ApplicationExtension extends Nette\DI\CompilerExtension
20: {
21: public $defaults = array(
22: 'debugger' => TRUE,
23: 'errorPresenter' => 'Nette:Error',
24: 'catchExceptions' => NULL,
25: 'mapping' => NULL,
26: 'scanDirs' => array(),
27: 'scanComposer' => NULL,
28: 'scanFilter' => 'Presenter',
29: 'silentLinks' => FALSE,
30: );
31:
32:
33: private $debugMode;
34:
35:
36: private $invalidLinkMode;
37:
38:
39: private $tempFile;
40:
41:
42: public function __construct($debugMode = FALSE, array $scanDirs = NULL, $tempDir = NULL)
43: {
44: $this->defaults['scanDirs'] = (array) $scanDirs;
45: $this->defaults['scanComposer'] = class_exists('Composer\Autoload\ClassLoader');
46: $this->defaults['catchExceptions'] = !$debugMode;
47: $this->debugMode = $debugMode;
48: $this->tempFile = $tempDir ? $tempDir . '/' . urlencode(__CLASS__) : NULL;
49: }
50:
51:
52: public function loadConfiguration()
53: {
54: $config = $this->validateConfig($this->defaults);
55: $container = $this->getContainerBuilder();
56: $container->addExcludedClasses(array('Nette\Application\UI\Control'));
57:
58: $this->invalidLinkMode = $this->debugMode
59: ? UI\Presenter::INVALID_LINK_TEXTUAL | ($config['silentLinks'] ? 0 : UI\Presenter::INVALID_LINK_WARNING)
60: : UI\Presenter::INVALID_LINK_WARNING;
61:
62: $application = $container->addDefinition($this->prefix('application'))
63: ->setClass('Nette\Application\Application')
64: ->addSetup('$catchExceptions', array($config['catchExceptions']))
65: ->addSetup('$errorPresenter', array($config['errorPresenter']));
66:
67: if ($config['debugger']) {
68: $application->addSetup('Nette\Bridges\ApplicationTracy\RoutingPanel::initializePanel');
69: }
70:
71: $touch = $this->debugMode && $config['scanDirs'] ? $this->tempFile : NULL;
72: $presenterFactory = $container->addDefinition($this->prefix('presenterFactory'))
73: ->setClass('Nette\Application\IPresenterFactory')
74: ->setFactory('Nette\Application\PresenterFactory', array(new Nette\DI\Statement(
75: 'Nette\Bridges\ApplicationDI\PresenterFactoryCallback', array(1 => $this->invalidLinkMode, $touch)
76: )));
77:
78: if ($config['mapping']) {
79: $presenterFactory->addSetup('setMapping', array($config['mapping']));
80: }
81:
82: $container->addDefinition($this->prefix('linkGenerator'))
83: ->setFactory('Nette\Application\LinkGenerator', array(
84: 1 => new Nette\DI\Statement('@Nette\Http\Request::getUrl'),
85: ));
86:
87: if ($this->name === 'application') {
88: $container->addAlias('application', $this->prefix('application'));
89: $container->addAlias('nette.presenterFactory', $this->prefix('presenterFactory'));
90: }
91: }
92:
93:
94: public function beforeCompile()
95: {
96: $container = $this->getContainerBuilder();
97: $all = array();
98:
99: foreach ($container->findByType('Nette\Application\IPresenter') as $def) {
100: $all[$def->getClass()] = $def;
101: }
102:
103: $counter = 0;
104: foreach ($this->findPresenters() as $class) {
105: if (empty($all[$class])) {
106: $all[$class] = $container->addDefinition($this->prefix(++$counter))->setClass($class);
107: }
108: }
109:
110: foreach ($all as $def) {
111: $def->setInject(TRUE)->setAutowired(FALSE)->addTag('nette.presenter', $def->getClass());
112: if (is_subclass_of($def->getClass(), 'Nette\Application\UI\Presenter')) {
113: $def->addSetup('$invalidLinkMode', array($this->invalidLinkMode));
114: }
115: }
116: }
117:
118:
119:
120: private function findPresenters()
121: {
122: $config = $this->getConfig();
123: $classes = array();
124:
125: if ($config['scanDirs']) {
126: $robot = new Nette\Loaders\RobotLoader;
127: $robot->setCacheStorage(new Nette\Caching\Storages\DevNullStorage);
128: $robot->addDirectory($config['scanDirs']);
129: $robot->acceptFiles = '*' . $config['scanFilter'] . '*.php';
130: $robot->rebuild();
131: $classes = array_keys($robot->getIndexedClasses());
132: $this->getContainerBuilder()->addDependency($this->tempFile);
133: }
134:
135: if ($config['scanComposer']) {
136: $rc = new \ReflectionClass('Composer\Autoload\ClassLoader');
137: $classFile = dirname($rc->getFileName()) . '/autoload_classmap.php';
138: if (is_file($classFile)) {
139: $this->getContainerBuilder()->addDependency($classFile);
140: $classes = array_merge($classes, array_keys(call_user_func(function($path) {
141: return require $path;
142: }, $classFile)));
143: }
144: }
145:
146: $presenters = array();
147: foreach (array_unique($classes) as $class) {
148: if (strpos($class, $config['scanFilter']) !== FALSE && class_exists($class)
149: && ($rc = new \ReflectionClass($class)) && $rc->implementsInterface('Nette\Application\IPresenter')
150: && !$rc->isAbstract()
151: ) {
152: $presenters[] = $rc->getName();
153: }
154: }
155: return $presenters;
156: }
157:
158: }
159: