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