Skip to content

Commit 8350eaf

Browse files
committed
default parameters (wwwDir, appDir, ...) are now overridable in user configs
Previously, parameters from getDefaultParameters() were injected into the compiler AFTER all user configs, which silently overrode any redefinition in common.neon. As a result, e.g. `wwwDir: %rootDir%/www` in a config had no effect when the entry script lived outside %rootDir%/www (CLI scripts, MCP servers, scheduled tasks). Fix: snapshot defaults into $defaultParameters at construction. In generateContainer(), inject defaults BEFORE user configs (overridable), then inject array_diff_key($staticParameters, $defaultParameters) AFTER configs as authoritative explicit overrides. Bookkeeping: setters (setTempDirectory, setDebugMode) route through addStaticParameters(), which removes the touched keys from $defaultParameters. That preserves the rule "what user explicitly set wins over configs" even when the explicit value coincidentally equals the auto-detected default. The container cache key includes the explicit set so two configurators with identical $staticParameters but different "user-touched" keys compile to distinct containers.
1 parent dd39047 commit 8350eaf

3 files changed

Lines changed: 88 additions & 8 deletions

File tree

src/Bootstrap/Configurator.php

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,13 @@ class Configurator
7676
/** @var list<string|array<string, mixed>> */
7777
protected array $configs = [];
7878

79+
/** @var array<string, mixed> */
80+
private array $defaultParameters;
81+
7982

8083
public function __construct()
8184
{
82-
$this->staticParameters = $this->getDefaultParameters();
85+
$this->defaultParameters = $this->staticParameters = $this->getDefaultParameters();
8386

8487
if (class_exists(InstalledVersions::class) // back compatibility
8588
&& InstalledVersions::isInstalled('nette/caching')
@@ -100,9 +103,10 @@ public function setDebugMode(bool|string|array $value): static
100103
$value = static::detectDebugMode($value);
101104
}
102105

103-
$this->staticParameters['debugMode'] = $value;
104-
$this->staticParameters['productionMode'] = !$this->staticParameters['debugMode']; // compatibility
105-
return $this;
106+
return $this->addStaticParameters([
107+
'debugMode' => $value,
108+
'productionMode' => !$value, // compatibility
109+
]);
106110
}
107111

108112

@@ -117,8 +121,7 @@ public function isDebugMode(): bool
117121
*/
118122
public function setTempDirectory(string $path): static
119123
{
120-
$this->staticParameters['tempDir'] = $path;
121-
return $this;
124+
return $this->addStaticParameters(['tempDir' => $path]);
122125
}
123126

124127

@@ -150,6 +153,7 @@ public function addParameters(array $params): static
150153
public function addStaticParameters(array $params): static
151154
{
152155
$this->staticParameters = DI\Config\Helpers::merge($params, $this->staticParameters);
156+
$this->defaultParameters = array_diff_key($this->defaultParameters, $params);
153157
return $this;
154158
}
155159

@@ -305,6 +309,8 @@ public function generateContainer(DI\Compiler $compiler): void
305309
$loader = $this->createLoader();
306310
$loader->setParameters($this->staticParameters);
307311

312+
$compiler->addConfig(['parameters' => DI\Helpers::escape($this->defaultParameters)]);
313+
308314
foreach ($this->configs as $config) {
309315
if (is_string($config)) {
310316
$compiler->loadConfig($config, $loader);
@@ -313,7 +319,8 @@ public function generateContainer(DI\Compiler $compiler): void
313319
}
314320
}
315321

316-
$compiler->addConfig(['parameters' => DI\Helpers::escape($this->staticParameters)]);
322+
$explicit = array_diff_key($this->staticParameters, $this->defaultParameters);
323+
$compiler->addConfig(['parameters' => DI\Helpers::escape($explicit)]);
317324
$compiler->setDynamicParameterNames(array_merge(array_keys($this->dynamicParameters), ['baseUrl']));
318325

319326
$builder = $compiler->getContainerBuilder();
@@ -344,6 +351,7 @@ protected function generateContainerKey(): array
344351
{
345352
return [
346353
$this->staticParameters,
354+
array_diff_key($this->staticParameters, $this->defaultParameters),
347355
array_keys($this->dynamicParameters),
348356
$this->configs,
349357
PHP_VERSION_ID - PHP_RELEASE_VERSION, // minor PHP version

tests/Bootstrap/Configurator.developmentContainer.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,9 @@ Assert::same([
3636
'wwwDir' => __DIR__,
3737
'vendorDir' => dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'vendor',
3838
'rootDir' => dirname(__DIR__, 2),
39+
'consoleMode' => PHP_SAPI === 'cli',
3940
'debugMode' => true,
4041
'productionMode' => false,
41-
'consoleMode' => PHP_SAPI === 'cli',
4242
'tempDir' => getTempDir(),
4343
], $container->parameters);
4444

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?php declare(strict_types=1);
2+
3+
use Nette\Bootstrap\Configurator;
4+
use Tester\Assert;
5+
6+
7+
require __DIR__ . '/../bootstrap.php';
8+
9+
10+
test('config overrides default wwwDir', function () {
11+
$configurator = new Configurator;
12+
$configurator->setTempDirectory(getTempDir());
13+
$configurator->addConfig([
14+
'parameters' => ['wwwDir' => '/from/config'],
15+
]);
16+
$container = $configurator->createContainer();
17+
Assert::same('/from/config', $container->parameters['wwwDir']);
18+
});
19+
20+
21+
test('addStaticParameters wins over config', function () {
22+
$configurator = new Configurator;
23+
$configurator->setTempDirectory(getTempDir());
24+
$configurator->addStaticParameters(['wwwDir' => '/from/static']);
25+
$configurator->addConfig([
26+
'parameters' => ['wwwDir' => '/from/config'],
27+
]);
28+
$container = $configurator->createContainer();
29+
Assert::same('/from/static', $container->parameters['wwwDir']);
30+
});
31+
32+
33+
test('setTempDirectory wins over config', function () {
34+
$tempDir = getTempDir();
35+
$configurator = new Configurator;
36+
$configurator->setTempDirectory($tempDir);
37+
$configurator->addConfig([
38+
'parameters' => ['tempDir' => '/from/config'],
39+
]);
40+
$container = $configurator->createContainer();
41+
Assert::same($tempDir, $container->parameters['tempDir']);
42+
});
43+
44+
45+
test('config can reference default parameter', function () {
46+
$configurator = new Configurator;
47+
$configurator->setTempDirectory(getTempDir());
48+
$configurator->addStaticParameters(['rootDir' => '/my/root']);
49+
$configurator->addConfig([
50+
'parameters' => ['wwwDir' => '%rootDir%/www'],
51+
]);
52+
$container = $configurator->createContainer();
53+
Assert::same('/my/root/www', $container->parameters['wwwDir']);
54+
});
55+
56+
57+
test('setter with default-equivalent value still wins over config', function () {
58+
$configurator = new Configurator;
59+
$configurator->setTempDirectory(getTempDir());
60+
61+
// pin debugMode to its current (default) value via the setter
62+
$defaultDebugMode = $configurator->isDebugMode();
63+
$configurator->setDebugMode($defaultDebugMode);
64+
65+
// config tries to flip it
66+
$configurator->addConfig([
67+
'parameters' => ['debugMode' => !$defaultDebugMode],
68+
]);
69+
70+
$container = $configurator->createContainer();
71+
Assert::same($defaultDebugMode, $container->parameters['debugMode']);
72+
});

0 commit comments

Comments
 (0)