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