Skip to content

Commit 3008ef8

Browse files
committed
added DI extensions auto-discovery
1 parent 93e17bf commit 3008ef8

File tree

3 files changed

+108
-1
lines changed

3 files changed

+108
-1
lines changed

composer.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@
5555
"extra": {
5656
"branch-alias": {
5757
"dev-master": "3.3-dev"
58+
},
59+
"nette": {
60+
"di-extensions": {
61+
"constants": "Nette\\Bootstrap\\Extensions\\ConstantsExtension",
62+
"php": "Nette\\Bootstrap\\Extensions\\PhpIniExtension"
63+
}
5864
}
5965
}
6066
}

src/Bootstrap/Configurator.php

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ class Configurator
7878
/** @var list<string|array<string, mixed>> */
7979
protected array $configs = [];
8080

81+
/** @var string[] extension names to exclude from auto-discovery */
82+
private array $excludeExtensions = [];
83+
private bool $autoDiscovery = true;
84+
8185

8286
public function __construct()
8387
{
@@ -175,6 +179,21 @@ public function addServices(array $services): static
175179
}
176180

177181

182+
/**
183+
* Disables auto-discovery of extensions from installed packages.
184+
* Without arguments disables completely, with arguments disables only specified extensions.
185+
*/
186+
public function excludeExtension(string ...$extensions): static
187+
{
188+
if (count($extensions) === 0) {
189+
$this->autoDiscovery = false;
190+
} else {
191+
$this->excludeExtensions = array_merge($this->excludeExtensions, $extensions);
192+
}
193+
return $this;
194+
}
195+
196+
178197
protected function getDefaultParameters(): array
179198
{
180199
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
@@ -255,6 +274,30 @@ public function addConfig(string|array $config): static
255274
}
256275

257276

277+
/**
278+
* Discovers extensions from installed Composer packages.
279+
* Reads extra.nette.di-extensions from vendor/composer/installed.json
280+
* @return array<string, string|array{class: class-string, args: array}>
281+
*/
282+
protected function discoverExtensions(): array
283+
{
284+
$vendorDir = $this->staticParameters['vendorDir'] ?? null;
285+
if (!$vendorDir || !is_file($installedJson = $vendorDir . '/composer/installed.json')) {
286+
return [];
287+
}
288+
289+
$installed = json_decode(Nette\Utils\FileSystem::read($installedJson), true);
290+
$extensions = [];
291+
foreach ($installed['packages'] ?? [] as $package) {
292+
foreach ($package['extra']['nette']['di-extensions'] ?? [] as $name => $def) {
293+
$extensions[$name] = is_string($def) ? $def : [$def['class'], $def['args'] ?? []];
294+
}
295+
}
296+
297+
return $extensions;
298+
}
299+
300+
258301
/**
259302
* Returns system DI container.
260303
*/
@@ -312,7 +355,10 @@ public function generateContainer(DI\Compiler $compiler): void
312355
$builder = $compiler->getContainerBuilder();
313356
$builder->addExcludedClasses($this->autowireExcludedClasses);
314357

315-
foreach ($this->defaultExtensions as $name => $extension) {
358+
$extensions = $this->autoDiscovery ? $this->discoverExtensions() : [];
359+
$extensions = array_merge($extensions, $this->defaultExtensions);
360+
$extensions = array_diff_key($extensions, $this->excludeExtensions);
361+
foreach ($extensions as $name => $extension) {
316362
[$class, $args] = is_string($extension)
317363
? [$extension, []]
318364
: $extension;
@@ -342,6 +388,7 @@ protected function generateContainerKey(): array
342388
class_exists(ClassLoader::class) // composer update
343389
? filemtime((new \ReflectionClass(ClassLoader::class))->getFilename())
344390
: null,
391+
$this->autoDiscovery ? $this->excludeExtensions : null,
345392
];
346393
}
347394

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
3+
/**
4+
* Test: Nette\Configurator::excludeExtension()
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
use Nette\Bootstrap\Configurator;
10+
use Tester\Assert;
11+
12+
13+
require __DIR__ . '/../bootstrap.php';
14+
15+
16+
test('excludeExtension() disables extension from defaultExtensions', function () {
17+
$configurator = new Configurator;
18+
$configurator->setTempDirectory(getTempDir());
19+
$configurator->excludeExtension('application');
20+
21+
$container = $configurator->createContainer();
22+
23+
// application extension should not be registered
24+
Assert::false($container->hasService('application'));
25+
Assert::false($container->hasService('nette.presenterFactory'));
26+
27+
// other extensions should still work
28+
Assert::true($container->hasService('httpRequest'));
29+
});
30+
31+
32+
test('excludeExtension() without arguments disables all auto-discovery', function () {
33+
$configurator = new Configurator;
34+
$configurator->setTempDirectory(getTempDir());
35+
$configurator->excludeExtension();
36+
37+
// defaultExtensions should still be registered
38+
$container = $configurator->createContainer();
39+
Assert::true($container->hasService('application'));
40+
Assert::true($container->hasService('httpRequest'));
41+
});
42+
43+
44+
test('excludeExtension() can disable multiple extensions', function () {
45+
$configurator = new Configurator;
46+
$configurator->setTempDirectory(getTempDir());
47+
$configurator->excludeExtension('application', 'mail');
48+
49+
$container = $configurator->createContainer();
50+
51+
Assert::false($container->hasService('application'));
52+
Assert::false($container->hasService('nette.mailer'));
53+
Assert::true($container->hasService('httpRequest'));
54+
});

0 commit comments

Comments
 (0)