Skip to content

Commit

Permalink
ParametersExtension, Container: redesigned way of handling dynamic pa…
Browse files Browse the repository at this point in the history
…rameters via getParameter()
  • Loading branch information
dg committed Sep 21, 2023
1 parent 2ef048c commit 7297fb8
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 46 deletions.
2 changes: 1 addition & 1 deletion src/DI/Compiler.php
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ public function getConfig(): array
public function setDynamicParameterNames(array $names)
{
assert($this->extensions[self::Parameters] instanceof Extensions\ParametersExtension);
$this->extensions[self::Parameters]->dynamicParams = $names;
$this->extensions[self::Parameters]->dynamicParams = array_flip($names);
return $this;
}

Expand Down
15 changes: 15 additions & 0 deletions src/DI/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,21 @@ public function getParameters(): array
}


public function getParameter($key)
{
if (!array_key_exists($key, $this->parameters)) {
$this->parameters[$key] = $this->getDynamicParameter($key);
}
return $this->parameters[$key];
}


protected function getDynamicParameter($key)
{
throw new Nette\InvalidStateException(sprintf("Parameter '%s' not found. Check if 'di › export › parameters' is enabled.", $key));
}


/**
* Adds the service to the container.
* @param object $service service or its factory
Expand Down
60 changes: 42 additions & 18 deletions src/DI/Extensions/ParametersExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
*/
final class ParametersExtension extends Nette\DI\CompilerExtension
{
/** @var string[] */
/** @var array */
public $dynamicParams = [];

/** @var string[][] */
Expand All @@ -38,8 +38,6 @@ public function loadConfiguration()
{
$builder = $this->getContainerBuilder();
$params = $this->config;
$resolver = new Nette\DI\Resolver($builder);
$generator = new Nette\DI\PhpGenerator($builder);

// expressions are automatically treated as dynamic params
foreach ($params as $key => $value) {
Expand All @@ -51,10 +49,8 @@ public function loadConfiguration()
});
}

foreach ($this->dynamicParams as $key) {
$params[$key] = array_key_exists($key, $params)
? new DynamicParameter($generator->formatPhp('($this->parameters[?] \?\? ?)', $resolver->completeArguments(Nette\DI\Helpers::filterArguments([$key, $params[$key]]))))
: new DynamicParameter((new Nette\PhpGenerator\Dumper)->format('$this->parameters[?]', $key));
foreach ($this->dynamicParams as $key => $foo) {
$params[$key] = new DynamicParameter((new Nette\PhpGenerator\Dumper)->format('$this->getParameter(?)', $key));
}

$builder->parameters = Nette\DI\Helpers::expand($params, $params, true);
Expand All @@ -68,21 +64,49 @@ public function loadConfiguration()

public function afterCompile(Nette\PhpGenerator\ClassType $class)
{
$parameters = $this->getContainerBuilder()->parameters;
array_walk_recursive($parameters, function (&$val): void {
if ($val instanceof Nette\DI\Definitions\Statement || $val instanceof DynamicParameter) {
$val = null;
}
});
$builder = $this->getContainerBuilder();

// static parameters
$staticParams = $builder->parameters;
foreach ($staticParams as $key => $value) {
$tmp = [$value];
array_walk_recursive($tmp, function ($val) use ($key, &$staticParams): void {
if ($val instanceof DynamicParameter) {
$this->dynamicParams[$key] = null;
unset($staticParams[$key]);
}
});
}

$cnstr = $class->getMethod('__construct');
$cnstr->addBody('$this->parameters += ?;', [$parameters]);
$cnstr->addBody('$this->parameters += ?;', [$staticParams]);

if (!$this->dynamicParams) {
return;
}

// dynamic parameters
$resolver = new Nette\DI\Resolver($builder);
$generator = new Nette\DI\PhpGenerator($builder);

$getter = $class->addMethod('getDynamicParameter')
->setProtected();
$getter->addParameter('key');
$body = 'switch (true) {';
foreach ($this->dynamicParams as $key => $foo) {
$value = Nette\DI\Helpers::expand($this->config[$key] ?? null, $builder->parameters);
$value = $resolver->completeArguments(Nette\DI\Helpers::filterArguments([$value]));
$body .= "\n\t" . 'case $key === ' . var_export($key, true) . ': return ' . $generator->formatPhp('?', $value) . ';';
}
$body .= "\n\t" . 'default: parent::getDynamicParameter($key);' . "\n};";
$getter->addBody($body);

// dynamic parameters validation
$initialize = $class->getMethod('initialize');
foreach ($this->dynamicValidators as [$param, $expected]) {
if ($param instanceof Nette\DI\Definitions\Statement) {
continue;
if (!$param instanceof Nette\DI\Definitions\Statement) {
$initialize->addBody('Nette\Utils\Validators::assert(?, ?, ?);', [$param, $expected, 'dynamic parameter']);
}

$cnstr->addBody('Nette\Utils\Validators::assert(?, ?, ?);', [$param, $expected, 'dynamic parameter']);
}
}
}
15 changes: 0 additions & 15 deletions tests/DI/Compiler.dynamicParameters.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -126,18 +126,3 @@ test('', function () {
');
Assert::same('hello', $container->getService('one')->arg);
});


test('', function () {
$compiler = new DI\Compiler;
$compiler->setDynamicParameterNames(['dynamic']);
Assert::exception(function () use ($compiler) {
createContainer($compiler, '
parameters:
dynamic: @one
services:
one: Service
');
}, Nette\DI\ServiceCreationException::class, "Reference to missing service 'one'.");
});
14 changes: 9 additions & 5 deletions tests/DI/Compiler.dynamicParameters.validator.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,12 @@ test('', function () {
$compiler->addExtension('foo', new FooExtension);
$compiler->setDynamicParameterNames(['dynamic']);
Assert::exception(function () use ($compiler) {
createContainer($compiler, '
$container = createContainer($compiler, '
foo:
key:
string: %dynamic%
', ['dynamic' => 123]);
$container->initialize();
}, Nette\Utils\AssertionException::class, 'The dynamic parameter expects to be string, int 123 given.');
});

Expand All @@ -42,11 +43,12 @@ test('', function () {
$compiler->addExtension('foo', new FooExtension);
$compiler->setDynamicParameterNames(['dynamic']);
Assert::exception(function () use ($compiler) {
createContainer($compiler, '
$container = createContainer($compiler, '
foo:
key:
string: %dynamic%
', ['dynamic' => null]);
$container->initialize();
}, Nette\Utils\AssertionException::class, 'The dynamic parameter expects to be string, null given.');
});

Expand All @@ -56,11 +58,12 @@ test('', function () {
$compiler->addExtension('foo', new FooExtension);
$compiler->setDynamicParameterNames(['dynamic']);
Assert::exception(function () use ($compiler) {
createContainer($compiler, '
$container = createContainer($compiler, '
foo:
key:
string: %dynamic.sub%
', ['dynamic' => ['sub' => 123]]);
$container->initialize();
}, Nette\Utils\AssertionException::class, 'The dynamic parameter expects to be string, int 123 given.');
});

Expand All @@ -70,11 +73,12 @@ test('', function () {
$compiler->addExtension('foo', new FooExtension);
$compiler->setDynamicParameterNames(['dynamic']);
Assert::noError(function () use ($compiler) {
createContainer($compiler, '
$container = createContainer($compiler, '
foo:
key:
intnull: %dynamic%
', ['dynamic' => 123]);
$container->initialize();
});
});

Expand All @@ -84,7 +88,7 @@ test('', function () {
$compiler->addExtension('foo', new FooExtension);
$compiler->setDynamicParameterNames(['dynamic']);
Assert::noError(function () use ($compiler) {
createContainer($compiler, '
$container = createContainer($compiler, '
foo:
key:
intnull: %dynamic%
Expand Down
18 changes: 12 additions & 6 deletions tests/DI/Compiler.parameters.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ test('', function () {
services:
one: Service(%bar%)
');
Assert::null($container->parameters['bar']);
Assert::same('a', $container->getParameter('bar'));
Assert::same('a', $container->getService('one')->arg);
});

Expand Down Expand Up @@ -66,7 +66,7 @@ test('', function () {
services:
one: Service(%bar%)
');
Assert::null($container->parameters['bar']);
Assert::same('Service::method hello', $container->getParameter('bar'));
Assert::same('Service::method hello', $container->getService('one')->arg);
});

Expand All @@ -81,7 +81,7 @@ test('', function () {
one: Service(%bar%)
two: Service(two)
');
Assert::same('@two', $container->parameters['bar']); // not resolved
Assert::same('@two', $container->getParameter('bar')); // not resolved
Assert::same($container->getService('two'), $container->getService('one')->arg);
});

Expand All @@ -96,7 +96,7 @@ test('', function () {
one: Service(%bar%)
two: Service(two)
');
Assert::null($container->parameters['bar']);
Assert::type(Service::class, $container->getParameter('bar'));
Assert::same($container->getService('two'), $container->getService('one')->arg->arg);
});

Expand All @@ -111,6 +111,12 @@ test('', function () {
one: Service(%bar%)
two: Service(two)
');
Assert::null($container->parameters['bar']);
Assert::same([$container->getService('two')], $container->getService('one')->arg);

Assert::exception(
function () use ($container) {
$container->getParameter('bar');
},
Nette\InvalidStateException::class,
'Circular reference detected for services: one.'
);
});
3 changes: 2 additions & 1 deletion tests/DI/DIExtension.exportParameters.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ test('', function () {
parameters: true
', ['dynamic' => 123]);

Assert::same(['dynamic' => 123, 'key' => null], $container->parameters);
Assert::same(['dynamic' => 123], $container->parameters);
Assert::same(123, $container->getParameter('key'));
});


Expand Down

0 comments on commit 7297fb8

Please sign in to comment.