Skip to content

Commit eb99abb

Browse files
committed
added DI extensions auto-discovery
1 parent 56d575e commit eb99abb

3 files changed

Lines changed: 108 additions & 1 deletion

File tree

composer.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@
5959
"extra": {
6060
"branch-alias": {
6161
"dev-master": "4.0-dev"
62+
},
63+
"nette": {
64+
"di-extensions": {
65+
"constants": "Nette\\Bootstrap\\Extensions\\ConstantsExtension",
66+
"php": "Nette\\Bootstrap\\Extensions\\PhpIniExtension"
67+
}
6268
}
6369
},
6470
"config": {

src/Bootstrap/Configurator.php

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ class Configurator
7979
/** @var array<string, mixed> */
8080
private array $defaultParameters;
8181

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

8387
public function __construct()
8488
{
@@ -180,6 +184,21 @@ public function addServices(array $services): static
180184
}
181185

182186

187+
/**
188+
* Disables auto-discovery of extensions from installed packages.
189+
* Without arguments disables completely, with arguments disables only specified extensions.
190+
*/
191+
public function excludeExtension(string ...$extensions): static
192+
{
193+
if (count($extensions) === 0) {
194+
$this->autoDiscovery = false;
195+
} else {
196+
$this->excludeExtensions = array_merge($this->excludeExtensions, $extensions);
197+
}
198+
return $this;
199+
}
200+
201+
183202
/** @return array<string, mixed> */
184203
protected function getDefaultParameters(): array
185204
{
@@ -265,6 +284,30 @@ public function addConfig(string|array $config): static
265284
}
266285

267286

287+
/**
288+
* Discovers extensions from installed Composer packages.
289+
* Reads extra.nette.di-extensions from vendor/composer/installed.json
290+
* @return array<string, string|array{class: class-string, args: array}>
291+
*/
292+
protected function discoverExtensions(): array
293+
{
294+
$vendorDir = $this->staticParameters['vendorDir'] ?? null;
295+
if (!$vendorDir || !is_file($installedJson = $vendorDir . '/composer/installed.json')) {
296+
return [];
297+
}
298+
299+
$installed = json_decode(Nette\Utils\FileSystem::read($installedJson), true);
300+
$extensions = [];
301+
foreach ($installed['packages'] ?? [] as $package) {
302+
foreach ($package['extra']['nette']['di-extensions'] ?? [] as $name => $def) {
303+
$extensions[$name] = is_string($def) ? $def : [$def['class'], $def['args'] ?? []];
304+
}
305+
}
306+
307+
return $extensions;
308+
}
309+
310+
268311
/**
269312
* Returns system DI container.
270313
*/
@@ -326,7 +369,10 @@ public function generateContainer(DI\Compiler $compiler): void
326369
$builder = $compiler->getContainerBuilder();
327370
$builder->addExcludedClasses($this->autowireExcludedClasses);
328371

329-
foreach ($this->defaultExtensions as $name => $extension) {
372+
$extensions = $this->autoDiscovery ? $this->discoverExtensions() : [];
373+
$extensions = array_merge($extensions, $this->defaultExtensions);
374+
$extensions = array_diff_key($extensions, $this->excludeExtensions);
375+
foreach ($extensions as $name => $extension) {
330376
[$class, $args] = is_string($extension)
331377
? [$extension, []]
332378
: $extension;
@@ -358,6 +404,7 @@ protected function generateContainerKey(): array
358404
class_exists(ClassLoader::class) // composer update
359405
? filemtime((new \ReflectionClass(ClassLoader::class))->getFilename())
360406
: null,
407+
$this->autoDiscovery ? $this->excludeExtensions : null,
361408
];
362409
}
363410

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)